The MMBase Function Framework


Table of Contents

1. What are functions ?
2. Some simple examples with the MMBase taglib
3. Function tag return types and parameters
4. Defining functions in builders
5. Defining functions in sets
6. Defining functions in modules
7. Using functions from the bridge
7.1. Function
7.2. Parameter
8. Working with nodes and and nodelists

The MMBase 'Function Framework' allows you to add functions or methods to different layers of MMBase. You can access them using the taglib function tags or the bridge. This tag <mm:function set="utils" name="generatePassword" /> returns a random password like 'zyrentu'.

Functions can be added to nodes, builders and clouds and can be defined in three locations:

Each having their own backend implementation but they all share a common way to use them from inside Java or JSP. This document shows some examples of the use of functions, more information can be found in the API documentation at org.mmbase.util.functions and the taglib reference documentation.

Allmost everybody who is bit familiar with the taglib knows that you can get the gui value of a node in MMBase 1.7 and former versions with a few simple lines of code.

<mm:node number="default.mags">
  <mm:field name="gui()" />
</mm:node>

This functionality is deprecated. The equivalent method of getting the gui value of a node in MMBase 1.8 is the following and as such is a method defined in MMObjectbuilder.

<mm:node number="default.mags">
  <mm:function name="gui" />
</mm:node>

Another simple example of a function is made available by the class org.mmbase.util.functions.ExampleBean and could look like this. It accesses the method stringFunction which uses no arguments and returns an arbitrary String.

<mm:function classname="org.mmbase.util.functions.ExampleBean" name="stringFunction" />

ExampleBean contains multiple methods that can be accessed using functions. ExampleBean.java contains a lot more lines of code of but when you strip it down to the bare minimum that is needed to execute the above example, the following lines of code remain.

package org.mmbase.util.functions;

    public final class ExampleBean {
    private String parameter1;
    private String parameter3 = "Default value";

    public void setParameter1(String hoi) {
        parameter1 = hoi;
    }

    public void setAnotherParameter(String a) {
        parameter3 = a;
    }

    public String stringFunction() {
        return "[[" + parameter1 + "/" + parameter3 + "]]";
    }

}

As you would expect this example function returns '[[null/Default value]]'.

A more exiting example would use the the set-methods defined in this bean by using parameter tags. These parameters need to be grouped with the functioncontainer tag.

  <mm:functioncontainer>
    <mm:param name="parameter1">foo</mm:param>
    <mm:param name="anotherParameter">bar</mm:param>
    <mm:function classname="org.mmbase.util.functions.ExampleBean" name="stringFunction" />
  </mm:functioncontainer> 

The above example returns '[[foo/bar]]'.

The class ExampleBean has several examples all with different return types. Multiple return types are defined by the way you call them. The taglib contains the following function tags to accommodate them.

  <mm:function          (String)
  <mm:booleanfunction   (boolean)
  <mm:voidfunction      (no return value)
  <mm:listfunction      (list)
  <mm:nodefunction      (a MMBase node or a virtual MMBase node)
  <mm:nodelistfunction  (a list of nodes or virtual nodes)

Functions can have parameters. The functioncontainer tag is designed to group functions and parameters. It also provides you with a way to group several functions together.

  <mm:functioncontainer>
    <mm:param name="template">AASSHHM</mm:param>
    your random new password might be : <mm:function set="utils" name="generatePassword" />
  </mm:functioncontainer>  

Each of the defined parameters will be added to every function within the functioncontainer.

It is also possible to add parameters using MMBase referids, this can come in handy when a variable is already present in your page and you want to access it.

  <mm:import externid="template">AASSHHM</mm:param>
  Your random new password might be:  
  <mm:function name="getPassword" referid="template" />         

Functions can be added to the builder configuration. This is especially usefull for methods related to a certain nodetype, allthough the following example is generic and can be added to every builder and thus every type of node.

To specify a function in a builder you should include a functionlist element defining a function with its 'name', a 'key' the method and a 'class' being the class of the method. The functionlist should be at the end of the builder configuration, after the fieldlist. The list can include of course multiple functions.

When you would include the following at the end of the buider 'mags.xml' in the MyNews example:

  <functionlist>
    <function key="stringFunction" name="bbb">
      <class>org.mmbase.util.functions.ExampleBean</class>
    </function>
  </functionlist>

You can use the following in a template:

  <mm:node number="default.mags"><mm:function name="bbb" /></mm:node>

This example uses the method stringFunction from ExampleBean (see above). It returns '[[null/default]]'.

Another example uses a class while including the class 'org.mmbase.util.functions.ExampleBuilder' in the class element of the 'news' builder.

  <class>org.mmbase.util.functions.ExampleBuilder</class>
You can use the following function in a template:

  <ol>
  <mm:listnodes type="news">
    <li>
      number: <mm:field name="number" />
      title: <mm:field name="title" />
      predecessor: <mm:function name="predecessor" />
    </li>
  </mm:listnodes>
  </ol> 

It returns the nodenumbers preceding the current one. The complete code of this example method can be found in the example class.

As should be clear from the above examples it is not especially necessary to define a function in a special builder class. Allthough because the function is only defined in the builder of the nodetype 'magazines', it only works on nodes of type 'magazines'. When you want your functions to be used by every type of node you better define a functionset.

You can check whether a node supports a certain function with the tag <mm:hasfunction />.

  <mm:hasfunction name="index">
    <mm:function name="index" />
  </hasfunction>
 

Unlike Module or NodeManagers related functions the set functions are aimed for a global use.

You can for example define a set 'utils' or 'statistics' or even map to external application 'lucene'. The set 'utils' is already definied in MMBase. Defining sets is done in a two xml files. The first in 'config/functions/functionsets.xml' defines what sets there are:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE functionsets 
  PUBLIC "-//MMBase//DTD functionsets config 1.0//EN" 
  "http://www.mmbase.org/dtd/functionsets_1_0.dtd">
<functionsets>
  <functionset name="utils"      resource="utils.xml" />
</functionsets> 
The set itself (in this case 'config/functions/utils.xml') defines the mapping, names, classes and methods to be called:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE functionset PUBLIC 
  "//MMBase - functionset //" "http://www.mmbase.org/dtd/functionset_1_0.dtd">
<functionset>

  <description>
   Some util functions from org.mmbase.util, made accessible through 
   the function-framework.
  </description>

  <function name="generatePassword">
    <description>
      Generates a password. Using a template (chain of characters A (random letter) C (random
      alphanumeric) H (random hex) S (random syllable) 6 (random dice) 9 (random digit)). Default
      template: SSSSSS.
    </description>
    <type>class</type>
    <class>org.mmbase.util.PasswordGenerator</class>
    <method>getPassword</method>
    <param name="template" type="String">SSSSSS</param>
  </function>

  <function name="randomLong">
    <description>
      Generates a random long integer.
    </description>
    <type>class</type>
    <class>org.mmbase.util.RandomPool</class>
    <method>value_and_stir</method>
  </function>

  <function name="filename">
    <description>
      Makes filename of commons file upload's FileItem.getName.
    </description>
    <type>class</type>
    <class>org.mmbase.util.functions.Utils</class>
    <method>getFileItemName</method>
    <param name="filename" type="String" />
  </function>

</functionset>     

As you can see the 'utils' functionset defines three functions that can be accessed using functiontags. For example like <mm:function set="utils" name="generatePassword" />.

Its also possible to define functions in MMBase Modules see e.g. the MMAdmin module and its jsp pages in mmbase/mmadmin/. Also e.g. the crontab and clustering applications ship with modules (CrontabModule, ClusteringModule), which define 'functions' which are used in admin pages.

The classes and interfaces of the function framework are in the org.mmbase.util.functions package. The bridge classes will return object from this package.

The most important classes are 'Function', and 'Parameter'.

One of the advantages of the function framework is that you can not only return objects like booleans or strings but also MMBase nodes or lists of nodes. In this way you can make functions that work like a <mm:node /> or <mm:nodelist /> but have their own selection method. For example we could make a function like:

  <mm:functionnode set="util" name="getRandomNode">
    object number : <mm:field name="number" />
    object type : <mm:field name="otype" />
    object owner : <mm:field name="owner" />
  </mm:functionnode>
   
Java snippet:
    public MMObjectNode getRandomNode() {
        return cloud.getRandomNode(randomnumber); // none working code
    }

The same is possible with lists :

  <mm:functionnodelist set="util" name="getRandomNodes">
    object number : <mm:field name="number" />
    object type : <mm:field name="otype" />
    object owner : <mm:field name="owner" />
  </mm:functionnodelist>
  
Again in Java the method could look like this :
    public List getRandomNodes() {
        List list =  new ArrayList();
        list.add(cloud.getRandomNode(randomnumber)); // none working code
        list.add(cloud.getRandomNode(randomnumber2)); // none working code
        list.add(cloud.getRandomNode(randomnumber3)); // none working code
        return list;
    }

Its also possible to use virtual nodes in functions, this opens up a interesting uses in that it allows you to return any information in the form of MMBase objects. The main advantage of this that the frontend programmers can use the tools they already know for handling nodes, fields and lists for example we could return some information as a mmbase node like this :

    public MMObjectNode getOSInfo() {
        Cloud cloud = LocalContext.getCloudContext().getCloud("mmbase");
        MMObjectNode virtual = builder.getNewNode("admin");
        virtual.setValue("hardware", System.getProperty("os.arch");
        virtual.setValue("os", System.getProperty("os.name");
        virtual.setValue("version", System.getProperty("os.version");
        return virtual;
    }

We can access methods using the MMBase taglib and handle the returns values like lists, nodes or fields.
  <mm:nodefunction set="mySet" name="getOSInfo">
    So you use <mm:field name="hardware" />
    <mm:field name="os">
      <mm:compare value="osx">
              and i guess you don't like Bill Gates.
      </mm:compare>
      <mm:compare value="windowsxp">
              and i guess you don't like Steve Jobs.
      </mm:compare>
    </mm:field>
  </mm:nodefunction>

We can also create lists. For example we could make a list of accounts from a LDAP server that acts like any normal MMBase list (made up code for the ldap parts).

    public List getAccounts(String searchkey) {
        Cloud cloud = LocalContext.getCloudContext().getCloud("mmbase");
        List list =  new ArrayList();
        // fill the list from the ldap server, using a while
        Iterator i = ldap.getAccounts(searchkey);
        while (i.hasNext()) {
            NextLDAP user = (NextLDAP)i.next();
            MMObjectNode virtual = builder.getNewNode("admin");
            virtual.setValue("account", user.getAccount();
            virtual.setValue("firstname", user.getFirstName();
            virtual.setValue("surname", user.getSurName();
            list.add(virtual);
        }
    }

From the taglib we can work with this list like we can work with any other nodelist :
  <mm:nodelistfunction set="mySet" name="getUsers" referid="searchkey">
    <mm:first>The first user found</mm:first>
    <mm:last>The last user found</mm:last>
    <mm:field name="account" /> (<mm:field name="firstname" /> <mm:field name="surname" />)<br />
  </mm:nodelistfunction>

As you can see all the normal MMBase tags can be applied. The frontend programmer doesn't have to know the content is not coming from MMBase at alle but from an LDAP server.


This is part of the MMBase documentation.

For questions and remarks about this documentation mail to: [email protected]