Some times the factories shipped with BASE are not enough, and you
may want to provide your own factory implementation. In this case you will
have to create a class that implements the
ActionFactory
package net.sf.basedb.examples.extensions; import net.sf.basedb.clients.web.extensions.JspContext; import net.sf.basedb.clients.web.extensions.menu.MenuItemAction; import net.sf.basedb.clients.web.extensions.menu.MenuItemBean; import net.sf.basedb.util.extensions.ActionFactory; import net.sf.basedb.util.extensions.InvokationContext; /** First example of an action factory where eveything is hardcoded. @author nicklas */ public class HelloWorldFactory implements ActionFactory<MenuItemAction> { private MenuItemAction[] helloWorld; // A public, no-argument constructor is required public HelloWorldFactory() { helloWorld = new MenuItemAction[1]; } // Return true enable the extension, false to disable it public boolean prepareContext( InvokationContext<? super MenuItemAction> context) { return true; } // An extension may create one or more actions public MenuItemAction[] getActions( InvokationContext<? super MenuItemAction> context) { // This cast is always safe with the web client JspContext jspContext = (JspContext)context.getClientContext(); if (helloWorld[0] == null) { MenuItemBean bean = new MenuItemBean(); bean.setTitle("Hello factory world!"); bean.setIcon(jspContext.getRoot() + "/images/info.gif"); bean.setOnClick("alert('Hello factory world!')"); helloWorld[0] = bean; } return helloWorld; } }
And here is the XML configuration file that goes with it.
<?xml version="1.0" encoding="UTF-8" ?> <extensions xmlns="http://base.thep.lu.se/extensions.xsd"> <extension id="net.sf.basedb.clients.web.menu.extensions.helloworldfactory" extends="net.sf.basedb.clients.web.menu.extensions" > <index>2</index> <about> <name>Hello factory world</name> <description> A "Hello world" variant with a custom action factory. Everything is hard-coded into the factory. </description> </about> <action-factory> <factory-class> net.sf.basedb.examples.extensions.HelloWorldFactory </factory-class> </action-factory> </extension> </extensions>
To install this extension you need to put the compiled
HelloWorldFactory.class
and the XML
file inside a JAR file. The XML file must be located at
META-INF/extensions.xml
and the class
file at net/sf/basedb/examples/extensions/HelloWorldFactory.class
.
The above example is a bit artificial and we have not gained anything. Instead, we have lost the ability to easily change the menu since everything is now hardcoded into the factory. To change, for example the title, requires that we recompile the java code. It would be more useful if we could make the factory configurable with parameters. The next example will make the icon and message configurable, and also include the name of the currently logged in user. For example: "Greetings <name of logged in user>!".
package net.sf.basedb.examples.extensions; import net.sf.basedb.clients.web.extensions.AbstractJspActionFactory; import net.sf.basedb.clients.web.extensions.menu.MenuItemAction; import net.sf.basedb.clients.web.extensions.menu.MenuItemBean; import net.sf.basedb.core.DbControl; import net.sf.basedb.core.SessionControl; import net.sf.basedb.core.User; import net.sf.basedb.util.extensions.ClientContext; import net.sf.basedb.util.extensions.InvokationContext; import net.sf.basedb.util.extensions.xml.PathSetter; import net.sf.basedb.util.extensions.xml.VariableSetter; /** Example menu item factory that creates a "Hello world" menu item where the "Hello" part can be changed by the "prefix" setting in the XML file, and the "world" part is dynamically replaced with the name of the logged in user. @author nicklas */ public class HelloUserFactory extends AbstractJspActionFactory<MenuItemAction> { // To store the URL to the icon private String icon; // The default prefix is Hello private String prefix = "Hello"; // A public, no-argument constructor is required public HelloUserFactory() {} /** Creates a menu item that displays: {prefix} {name of user}! */ public MenuItemAction[] getActions( InvokationContext<? super MenuItemAction> context) { String userName = getUserName(context.getClientContext()); MenuItemBean helloUser = new MenuItemBean(); helloUser.setTitle(prefix + " " + userName + "!"); helloUser.setIcon(icon); helloUser.setOnClick("alert('" + prefix + " " + userName + "!')"); return new MenuItemAction[] { helloUser }; } /** Get the name of the logged in user. */ private String getUserName(ClientContext context) { SessionControl sc = context.getSessionControl(); DbControl dc = context.getDbControl(); User current = User.getById(dc, sc.getLoggedInUserId()); return current.getName(); } /** Sets the icon to use. Path conversion is enabled. */ @VariableSetter @PathSetter public void setIcon(String icon) { this.icon = icon; } /** Sets the prefix to use. If not set, the default value is "Hello". */ public void setPrefix(String prefix) { this.prefix = prefix == null ? "Hello" : prefix; } }
The are two new parts in this factory. The first is the getUserName()
method which is called from getActions()
. Note
that the getActions()
method always create
a new MenuItemBean
The second new part is the setIcon()
and
setPrefix()
methods.
The extensions system uses java reflection to find the existance of
the methods if <icon>
and/or
<prefix>
tags are present
in the <parameters>
tag for a factory,
the methods are automatically called with the value inside the tag
as it's argument.
The VariableSetter
PathSetter
setIcon()
are used to enable
"smart" convertions of the value. Note that in the
XML file you only have to specify /images/info.gif
as the URL to the icon, but in the hardcoded factory you have to
do: jspContext.getRoot() + "/images/info.gif"
. In this case,
it is the PathSetter
which automatically adds the
the JSP root directory to all URL:s starting with /. The
VariableSetter
can do the same thing but you would have to use
$ROOT$
instead. Eg. $ROOT$/images/info.gif
. The
PathSetter
only looks at the first characteer,
while the VariableSetter
looks in the entire string.
Here is an example of an extension configuration that can be used with the new factory.
<extensions xmlns="http://base.thep.lu.se/extensions.xsd"> <extension id="net.sf.basedb.clients.web.menu.extensions.hellouser" extends="net.sf.basedb.clients.web.menu.extensions" > <index>3</index> <about> <name>Greetings user</name> <description> A "Hello world" variant with a custom action factory that displays "Greetings {name of user}" instead. We also make the icon configurable. </description> </about> <action-factory> <factory-class> net.sf.basedb.examples.extensions.HelloUserFactory </factory-class> <parameters> <prefix>Greetings</prefix> <icon>/images/take_ownership.png</icon> </parameters> </action-factory> </extension> </extensions>
Be aware of multi-threading issues | |
---|---|
When you are creating custom action and renderer factories be aware that multiple threads may use a single factory instance at the same time. Action and renderer objects only needs to be thread-safe if the factories re-use the same objects. |