source: trunk/doc/src/docbook/developer/extensions.xml @ 5781

Last change on this file since 5781 was 5781, checked in by Nicklas Nordborg, 10 years ago

References #1590: Documentation cleanup

Moved "Internals of the Core API" to "The BASE API" section.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Id
File size: 66.9 KB
Line 
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE chapter PUBLIC
3    "-//Dawid Weiss//DTD DocBook V3.1-Based Extension for XML and graphics inclusion//EN"
4    "../../../../lib/docbook/preprocess/dweiss-docbook-extensions.dtd">
5<!--
6  $Id: extensions.xml 5781 2011-10-04 12:00:07Z nicklas $
7 
8  Copyright (C) 2008
9 
10  This file is part of BASE - BioArray Software Environment.
11  Available at http://base.thep.lu.se/
12 
13  BASE is free software; you can redistribute it and/or
14  modify it under the terms of the GNU General Public License
15  as published by the Free Software Foundation; either version 3
16  of the License, or (at your option) any later version.
17 
18  BASE is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  GNU General Public License for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with BASE. If not, see <http://www.gnu.org/licenses/>.
25-->
26
27<chapter id="extensions_developer">
28  <title>Extensions developer</title>
29
30  <sect1 id="extensions_developer.overview">
31    <title>Overview</title>
32    <para>
33      The BASE application includes an extensions mechanism that makes it possible
34      to dynamically add functions to the GUI without having to edit the JSP code.
35      It is, for example, possible to add menu items in the menu and toolbar buttons
36      in selected toolbars.
37    </para>
38   
39    <para>
40      Go to the
41        <menuchoice>
42          <guimenu>Administrate</guimenu>
43          <guimenuitem>Plug-ins &amp; extensions</guimenuitem>
44          <guimenuitem>Overview</guimenuitem>
45        </menuchoice>
46      menu to display a list of possible extension points and all installed
47      extensions. From this page, if you are logged in with enough permissions,
48      it is also possible to configure the extensions system, enable/disable
49      extensions, etc. Read more about this in <xref linkend="plugins" />.
50    </para>
51
52    <para>
53      Extensions can come in two forms, either as an XML file in the
54      <emphasis>BASE Extensions XML</emphasis> format or as a JAR file.
55      A JAR file is required when the extension needs to execute custom-made
56      code or use custom resources such as icons, css stylesheets, or
57      JSP files.
58    </para>
59
60    <itemizedlist>
61      <title>More reading</title>
62      <listitem>
63        <para>
64        <xref linkend="plugins" />.
65        </para>
66      </listitem>
67     
68      <listitem>
69        <para>
70        <xref linkend="core_api.extensions" />.
71        </para>
72      </listitem>
73    </itemizedlist>
74
75    <sect2 id="extensions_developer.examplecode">
76      <title>Download code examples</title>
77
78      <para>
79        The code examples in this chapter can be downloaded from the
80        BASE plug-ins site:
81        <ulink url="http://baseplugins.thep.lu.se/wiki/net.sf.basedb.examples.extensions"
82        >http://baseplugins.thep.lu.se/wiki/net.sf.basedb.examples.extensions</ulink>
83      </para>
84    </sect2>
85
86    <sect2 id="extensions_developer.terminology">
87      <title>Terminology</title>
88     
89      <variablelist>
90      <varlistentry>
91        <term>Extension point</term>
92        <listitem>
93          <para>
94          An extensions point is a place in the BASE core or web client interface
95          where it is possible to extend the functionality with custom extensions. An
96          extension point has an ID which must be unique among all existing
97          extension points. Extension points registered by the BASE web client all
98          starts with <code>net.sf.basedb.clients.web</code> prefix. The
99          extension point also defines an
100          <interfacename docapi="net.sf.basedb.util.extensions">Action</interfacename>
101          subclass that all extensions must implement.
102          </para>
103        </listitem>
104      </varlistentry>
105     
106      <varlistentry>
107        <term>Extension</term>
108        <listitem>
109          <para>
110          An extensions is a custom addition to the BASE web client
111          interface or core API. This can mean a new menu item in the menu or a
112          new button in a toolbar. An extension must provide an
113          <interfacename docapi="net.sf.basedb.util.extensions">ActionFactory</interfacename>
114          that knows how to create actions that fits the requirements
115          from the extension point the extension is extending.
116          </para>
117        </listitem>
118      </varlistentry>
119     
120      <varlistentry>
121        <term>Action</term>
122        <listitem>
123          <para>
124          An <interfacename docapi="net.sf.basedb.util.extensions">Action</interfacename>
125          is an interface definition which provides an extension point enough
126          information to make it possible to render the action as HTML.
127          An action typically has methods such as, <code>getTitle()</code>,
128          <code>getIcon()</code> and <code>getOnClick()</code>.
129          </para>
130        </listitem>
131      </varlistentry>
132     
133      <varlistentry>
134        <term>Action factory</term>
135        <listitem>
136          <para>
137          An <interfacename docapi="net.sf.basedb.util.extensions">ActionFactory</interfacename>
138          is an object that knows how to create actions of some specific type, for
139          example menu item actions. Action factories are part of an
140          extension definition and can usually be configured with parameters
141          from the XML file. BASE ships with several implementations of
142          action factories for all defined extension points. Still, if your
143          extension needs a different implementation you can easily create your
144          own factory.
145          </para>
146        </listitem>
147      </varlistentry>
148     
149      <varlistentry>
150        <term>Renderer</term>
151        <listitem>
152          <para>
153          A <interfacename docapi="net.sf.basedb.util.extensions">Renderer</interfacename>
154          is an object that knows how to convert the information in an action to
155          HTML. The use of renderers are optional. Some extension points use a
156          "hard-coded" approach that renders the actions directly on the JSP file.
157          Some extension points uses a locked renderer, while other extension
158          points provides a default renderer, but allows extensions to supply their
159          own if they want to. Renderers are mostly used by the web client extensions,
160          not so much by the core extensions.
161          </para>
162        </listitem>
163      </varlistentry>
164 
165      <varlistentry>
166        <term>Renderer factory</term>
167        <listitem>
168          <para>
169          A <interfacename docapi="net.sf.basedb.util.extensions">RendererFactory</interfacename>
170          is an object that knows how to create renderers. Renderer factories can
171          be part of both extension points and extensions. In most other aspects
172          renderer factories are very similar to action factories.
173          </para>
174        </listitem>
175      </varlistentry>
176
177      <varlistentry>
178        <term>Error handler factory</term>
179        <listitem>
180          <para>
181          An <interfacename docapi="net.sf.basedb.util.extensions">ErrorHandlerFactory</interfacename>
182          is an object that knows how to handle error that occur when executing extensions.
183          An error handler factory is defined as part of an extension point and handles all
184          errors related to the extensions of that extension point. In most other aspects
185          error handler factories are similar to renderer and action factories. If the
186          extension point doesn't define an error handler factory, the system will
187          select a default that only writes a message to the log file
188          <classname docapi="net.sf.basedb.util.extensions">LoggingErrorHandlerFactory</classname>
189          </para>
190        </listitem>
191      </varlistentry>
192     
193      <varlistentry>
194        <term>Client context</term>
195        <listitem>
196          <para>
197          A <classname docapi="net.sf.basedb.util.extensions">ClientContext</classname>
198          is an object which contains information about the current user session.
199          It is for example, possible to get information about the logged in
200          user, the currently active item, etc.
201          </para>
202         
203          <para>
204          In the BASE web client the context is always a
205          <classname docapi="net.sf.basedb.clients.web.extensions">JspContext</classname>.
206          Wherever a <classname>ClientContext</classname> object is provided as a
207          parameter it is always safe to cast it to a <classname>JspContext</classname> 
208          object. Extension points in the core usually use a
209          <classname>ClientContext</classname>.
210          </para>
211         
212          <para>
213          The context can also be used by an extension to request that a specific
214          javascript or stylesheet file should be included in the HTML code.
215          </para>
216        </listitem>
217      </varlistentry>
218     
219      </variablelist>
220     
221     
222    </sect2>
223  </sect1>
224 
225  <sect1 id="extensions_developer.helloworld">
226    <title>Hello world as an extension</title>
227   
228    <para>
229      We will use the classical Hello world as the first simple example
230      of an extension. This extension will add a new menu item in the
231      menu which displays a popup with the text "Hello world!" when
232      selected. Copy the XML code below and save it to a file in
233      the <filename>plugins.dir</filename>
234      directory. The filename must end with <filename>.xml</filename>.
235      Install the extension by going through the installation wizard
236      at <menuchoice>
237          <guimenu>Administrate</guimenu>
238          <guimenuitem>Plug-ins &amp; extensions</guimenuitem>
239          <guimenuitem>Overview</guimenuitem>
240        </menuchoice>.
241    </para>
242   
243    <para>
244      When the extension has been installed you should have a new
245      menu item:
246      <menuchoice>
247        <guimenu>Extensions</guimenu>
248        <guimenuitem>Hello world!</guimenuitem>
249      </menuchoice>
250      which pops up a message in a Javascript window.
251    </para>
252   
253    <note>
254      <para>
255      You may have to logout and login again to see the new menu item.
256      </para>
257    </note>
258   
259    <programlisting language="xml">
260<![CDATA[
261<?xml version="1.0" encoding="UTF-8" ?>
262<extensions xmlns="http://base.thep.lu.se/extensions.xsd">
263   <extension
264      id="net.sf.basedb.clients.web.menu.extensions.helloworld"
265      extends="net.sf.basedb.clients.web.menu.extensions"
266      >
267      <index>1</index>
268      <about>
269         <name>Hello world</name>
270         <description>
271            The very first extensions example. Adds a "Hello world"
272            menu item that displays "Hello world" in a javascript
273            popup when selected.
274         </description>
275      </about>
276      <action-factory>
277         <factory-class> 
278            net.sf.basedb.clients.web.extensions.menu.FixedMenuItemFactory
279         </factory-class>
280         <parameters>
281            <title>Hello world!</title>
282            <tooltip>This is to test the extensions system</tooltip>
283            <onClick>alert('Hello world!')</onClick>
284            <icon>/images/info.gif</icon>
285         </parameters>
286      </action-factory>
287   </extension>
288</extensions>
289]]>
290</programlisting>
291   
292    <para>
293      The <sgmltag class="starttag">extensions</sgmltag> tag is the root
294      tag and is needed to set up the namespace and schema validation.
295    </para>
296   
297    <para>
298      The <sgmltag class="starttag">extension</sgmltag> defines a new
299      extension. It must have an <sgmltag>id</sgmltag> attribute that
300      is unique among all installed extensions and an <sgmltag>extends</sgmltag> 
301      attribute which id the ID of the extension point. For the <sgmltag>id</sgmltag>
302      attribute we recommend using the same naming conventions as for java
303      packages. See <ulink 
304      url="http://www.oracle.com/technetwork/java/codeconventions-135099.html">Java naming
305      conventions from Oracle</ulink>.
306    </para>
307   
308    <para>
309      The <sgmltag class="starttag">about</sgmltag> tag is optional and
310      can be used to provide meta information about the extension. We
311      recommend that all extensions are given at least a
312      <sgmltag class="starttag">name</sgmltag>. Other supported subtags are:
313     
314      <itemizedlist>
315        <listitem><sgmltag class="starttag">description</sgmltag></listitem>
316        <listitem><sgmltag class="starttag">version</sgmltag></listitem>
317        <listitem><sgmltag class="starttag">copyright</sgmltag></listitem>
318        <listitem><sgmltag class="starttag">contact</sgmltag></listitem>
319        <listitem><sgmltag class="starttag">email</sgmltag></listitem>
320        <listitem><sgmltag class="starttag">url</sgmltag></listitem>
321      </itemizedlist>
322     
323      <tip>
324        <title>Global about tag</title>
325       
326        <para>
327          <sgmltag class="starttag">about</sgmltag> tag can also be specified as a
328          first-level tag (eq. as a child to <sgmltag class="starttag">extensions</sgmltag>).
329          This can be useful when an XML file defines more than one extension
330          and you don't want to repeat the same information for every extension.
331          You can still override the information for specific extensions by including
332          new values in the extension's <sgmltag class="starttag">about</sgmltag>
333          tag.
334        </para>
335      </tip>
336    </para>
337   
338    <para>
339      The <sgmltag class="starttag">action-factory</sgmltag> tag is required and
340      so is the <sgmltag class="starttag">factory-class</sgmltag> subtag. It tells
341      the extension system which factory to use for creating actions. The
342      <classname docapi="net.sf.basedb.clients.web.extensions.menu">FixedMenuItemFactory</classname>
343      is a very simple factory that is shipped with BASE. This factory always creates the same
344      menu item, no matter what. Another factory for menu items is the
345      <classname docapi="net.sf.basedb.clients.web.extensions.menu">PermissionMenuItemFactory</classname>
346      which can create menu items that depends on the logged in user's permissions. It is
347      for example, possible to hide or disable the menu item if the user doesn't have enough
348      permissions. If none of the supplied factories suits you it is possible to
349      supply your own implementation. More about this later.
350    </para>
351   
352    <para>
353      The <sgmltag class="starttag">parameters</sgmltag> subtag is used to
354      provide initialisation parameters to the factory. Different factories
355      supports different parameters and you will have to check the javadoc documentation
356      for each factory to get information about which parameters that are supported.
357    </para>
358   
359    <tip>
360      <para>
361        In case the factory is poorly documented you can always assume that public
362        methods the start with <code>set</code> and take a single <classname>String</classname>
363        as an argument can be used as a parameter. The parameter tag to use should
364        be the same as the method name, minus the <code>set</code> prefix and with
365        the first letter in lowercase. For example, the method
366        <methodname>setIcon(String icon)</methodname> corresponds to the
367        <sgmltag class="starttag">icon</sgmltag> parameter.
368      </para>
369    </tip>
370   
371    <sect2 id="extensions_developer.extend_multiple">
372      <title>Extending multiple extension points with a single extension</title>
373     
374      <para>
375        A single extension can extend multiple extension points as long as their
376        action classes are compatible. This is for, for example, the case when
377        you want to add a button to more than one toolbar.       
378        To do this use the <sgmltag class="starttag">extends</sgmltag>
379        tag with multiple <sgmltag class="starttag">ref</sgmltag> tags.
380        You can skip the <sgmltag class="attribute">extends</sgmltag> 
381        attribute in the main tag.
382      </para>
383    <programlisting language="xml">
384<![CDATA[
385<extension
386   id="net.sf.basedb.clients.web.menu.extensions.history-edit"
387   >
388   <extends>
389      <ref index="2">net.sf.basedb.clients.web.tabcontrol.edit.sample</ref>
390      <ref index="2">net.sf.basedb.clients.web.tabcontrol.edit.extract</ref>
391   </extends>
392   ...
393</extension>
394]]>
395</programlisting>
396     
397      <para>
398        This is a feature of the XML format only. Behind the scenes two extensions will
399        be created (one for each extension point). The extensions will share the same
400        action and renderer factory instances. Since the id for an extension must be unique
401        a new id will be generated by combining the original id with the parts of the id's from
402        the extension points.
403      </para>
404     
405    </sect2>
406  </sect1>
407
408
409  <sect1 id="extensions_developer.factories">
410    <title>Custom action factories</title>
411
412    <para>
413      Some times the factories shipped with BASE are not enough, and you
414      may want to provide your own factory implementation. In this case you will
415      have to create a class that implements the
416      <interfacename docapi="net.sf.basedb.util.extensions">ActionFactory</interfacename>
417      interface. Here is a very simple example that does the same as the
418      previous "Hello world" example.
419    </para>
420   
421    <programlisting language="java">
422<![CDATA[
423package net.sf.basedb.examples.extensions;
424
425import net.sf.basedb.clients.web.extensions.JspContext;
426import net.sf.basedb.clients.web.extensions.menu.MenuItemAction;
427import net.sf.basedb.clients.web.extensions.menu.MenuItemBean;
428import net.sf.basedb.util.extensions.ActionFactory;
429import net.sf.basedb.util.extensions.InvokationContext;
430
431/**
432   First example of an action factory where eveything is hardcoded.
433   @author nicklas
434*/
435public class HelloWorldFactory
436   implements ActionFactory<MenuItemAction>
437{
438
439   private MenuItemAction[] helloWorld;
440
441   // A public, no-argument constructor is required
442   public HelloWorldFactory()
443   {
444      helloWorld = new MenuItemAction[1];
445   }
446   
447   // Return true enable the extension, false to disable it
448   public boolean prepareContext(
449      InvokationContext<? super MenuItemAction> context)
450   {
451      return true;
452   }
453
454   // An extension may create one or more actions
455   public MenuItemAction[] getActions(
456      InvokationContext<? super MenuItemAction> context)
457   {
458      // This cast is always safe with the web client
459      JspContext jspContext = (JspContext)context.getClientContext();
460      if (helloWorld[0] == null)
461      {
462         MenuItemBean bean = new MenuItemBean();
463         bean.setTitle("Hello factory world!");
464         bean.setIcon(jspContext.getRoot() + "/images/info.gif");
465         bean.setOnClick("alert('Hello factory world!')");
466         helloWorld[0] = bean;
467      }
468      return helloWorld;
469   }
470}
471]]>
472</programlisting>
473
474    <para>
475      And here is the XML configuration file that goes with it.
476    </para>
477
478    <programlisting language="xml">
479<![CDATA[
480<?xml version="1.0" encoding="UTF-8" ?>
481<extensions xmlns="http://base.thep.lu.se/extensions.xsd">
482   <extension
483      id="net.sf.basedb.clients.web.menu.extensions.helloworldfactory"
484      extends="net.sf.basedb.clients.web.menu.extensions"
485      >
486      <index>2</index>
487      <about>
488         <name>Hello factory world</name>
489         <description>
490            A "Hello world" variant with a custom action factory.
491            Everything is hard-coded into the factory.
492         </description>
493      </about>
494      <action-factory>
495         <factory-class>
496            net.sf.basedb.examples.extensions.HelloWorldFactory
497         </factory-class>
498      </action-factory>
499   </extension>
500</extensions>
501]]>
502</programlisting>
503   
504    <para>
505      To install this extension you need to put the compiled
506      <filename>HelloWorldFactory.class</filename> and the XML
507      file inside a JAR file. The XML file must be located at
508      <filename>META-INF/extensions.xml</filename> and the class
509      file at <filename>net/sf/basedb/examples/extensions/HelloWorldFactory.class</filename>.
510    </para>
511   
512    <para>
513      The above example is a bit artificial and we have not gained anything.
514      Instead, we have lost the ability to easily change the menu since everything is
515      now hardcoded into the factory. To change, for example the title, requires that
516      we recompile the java code. It would be more useful if we could make the factory
517      configurable with parameters. The next example will make the icon
518      and message configurable, and also include the name
519      of the currently logged in user. For example: "Greetings &lt;name of
520      logged in user&gt;!".
521    </para>
522   
523    <programlisting language="java">
524<![CDATA[
525package net.sf.basedb.examples.extensions;
526
527import net.sf.basedb.clients.web.extensions.AbstractJspActionFactory;
528import net.sf.basedb.clients.web.extensions.menu.MenuItemAction;
529import net.sf.basedb.clients.web.extensions.menu.MenuItemBean;
530import net.sf.basedb.core.DbControl;
531import net.sf.basedb.core.SessionControl;
532import net.sf.basedb.core.User;
533import net.sf.basedb.util.extensions.ClientContext;
534import net.sf.basedb.util.extensions.InvokationContext;
535import net.sf.basedb.util.extensions.xml.PathSetter;
536import net.sf.basedb.util.extensions.xml.VariableSetter;
537
538/**
539   Example menu item factory that creates a "Hello world" menu item
540   where the "Hello" part can be changed by the "prefix" setting in the
541   XML file, and the "world" part is dynamically replaced with the name
542   of the logged in user.
543   
544   @author nicklas
545*/
546public class HelloUserFactory
547   extends AbstractJspActionFactory<MenuItemAction>
548{
549   // To store the URL to the icon
550   private String icon;
551   
552   // The default prefix is Hello
553   private String prefix = "Hello";
554   
555   // A public, no-argument constructor is required
556   public HelloUserFactory()
557   {}
558   
559   /**
560      Creates a menu item that displays: {prefix} {name of user}!
561   */
562   public MenuItemAction[] getActions(
563      InvokationContext<? super MenuItemAction> context)
564   {
565      String userName = getUserName(context.getClientContext());
566      MenuItemBean helloUser = new MenuItemBean();
567      helloUser.setTitle(prefix + " " + userName + "!");
568      helloUser.setIcon(icon);
569      helloUser.setOnClick("alert('" + prefix + " " + userName + "!')");
570      return new MenuItemAction[] { helloUser };
571   }
572
573   /**
574      Get the name of the logged in user.
575   */
576   private String getUserName(ClientContext context)
577   {
578      SessionControl sc = context.getSessionControl();
579      DbControl dc = context.getDbControl();
580      User current = User.getById(dc, sc.getLoggedInUserId());
581      return current.getName();
582   }
583   
584   /**
585      Sets the icon to use. Path conversion is enabled.
586   */
587   @VariableSetter
588   @PathSetter
589   public void setIcon(String icon)
590   {
591      this.icon = icon;
592   }
593   
594   /**
595      Sets the prefix to use. If not set, the
596      default value is "Hello".
597   */
598   public void setPrefix(String prefix)
599   {
600      this.prefix = prefix == null ? "Hello" : prefix;
601   }
602}
603]]>
604</programlisting>
605   
606    <para>
607      The are two new parts in this factory. The first is the <methodname>getUserName()</methodname>
608      method which is called from <methodname>getActions()</methodname>. Note
609      that the <methodname>getActions()</methodname> method always create
610      a new <classname docapi="net.sf.basedb.clients.web.extensions.menu">MenuItemBean</classname>.
611      It can no longer be cached since the title and javascript code depends on
612      which user is logged in.
613    </para>
614   
615    <para>
616      The second new part is the <methodname>setIcon()</methodname> and
617      <methodname>setPrefix()</methodname> methods.
618      The extensions system uses java reflection to find the existance of
619      the methods if <sgmltag class="starttag">icon</sgmltag> and/or
620      <sgmltag class="starttag">prefix</sgmltag> tags are present
621      in the <sgmltag class="starttag">parameters</sgmltag> tag for a factory,
622      the methods are automatically called with the value inside the tag
623      as it's argument.
624    </para>
625   
626    <para>
627      The <classname docapi="net.sf.basedb.util.extensions.xml">VariableSetter</classname> 
628      and <classname docapi="net.sf.basedb.util.extensions.xml">PathSetter</classname> 
629      annotations on the <methodname>setIcon()</methodname> are used to enable
630      "smart" convertions of the value. Note that in the
631      XML file you only have to specify <filename>/images/info.gif</filename>
632      as the URL to the icon, but in the hardcoded factory you have to
633      do: <code>jspContext.getRoot() + "/images/info.gif"</code>. In this case,
634      it is the <classname>PathSetter</classname> which automatically adds the
635      the JSP root directory to all URL:s starting with /. The
636      <classname>VariableSetter</classname> can do the same thing but you would have to use
637      <code>$ROOT$</code> instead. Eg. <code>$ROOT$/images/info.gif</code>. The
638      <classname>PathSetter</classname> only looks at the first characteer,
639      while the <classname>VariableSetter</classname> looks in the entire string.
640    </para>
641   
642    <para>
643      Here is an example of an extension configuration that can be used
644      with the new factory.
645    </para>
646   
647    <programlisting language="xml">
648<![CDATA[
649<extensions xmlns="http://base.thep.lu.se/extensions.xsd">
650   <extension
651      id="net.sf.basedb.clients.web.menu.extensions.hellouser"
652      extends="net.sf.basedb.clients.web.menu.extensions"
653      >
654      <index>3</index>
655      <about>
656         <name>Greetings user</name>
657         <description>
658            A "Hello world" variant with a custom action factory
659            that displays "Greetings {name of user}" instead. We also
660            make the icon configurable.
661         </description>
662      </about>
663      <action-factory>
664         <factory-class>
665            net.sf.basedb.examples.extensions.HelloUserFactory
666         </factory-class>
667         <parameters>
668            <prefix>Greetings</prefix>
669            <icon>/images/take_ownership.png</icon>
670         </parameters>
671      </action-factory>
672   </extension>
673</extensions>
674]]>
675</programlisting>
676   
677    <note>
678      <title>Be aware of multi-threading issues</title>
679      <para>
680        When you are creating custom action and renderer factories be
681        aware that multiple threads may use a single factory instance
682        at the same time. Action and renderer objects only needs to
683        be thread-safe if the factories re-use the same objects.
684      </para>
685    </note>
686   
687  </sect1>
688
689  <sect1 id="extensions_developer.resources">
690    <title>Custom images, JSP files, and other resources</title>
691
692    <para>
693      Some times your extension may need other resources. It can for
694      for example be an icon, a javascript file, a JSP file or something
695      else. Fortunately this is very easy. You need to put the extension
696      in a JAR file. As usual the extension definition XML file should be
697      at <filename>META-INF/extensions.xml</filename>. Everything you put in
698      the JAR file inside the <filename>resources/</filename> directory
699      will automatically be extracted by the extension system to a directory
700      on the web server. Here is another "Hello world" example which uses a
701      custom JSP file to display the message. There is also a custom icon.
702    </para>
703   
704    <programlisting language="xml">
705<![CDATA[
706<extensions xmlns="http://base.thep.lu.se/extensions.xsd">
707   <extension
708      id="net.sf.basedb.clients.web.menu.extensions.hellojspworld"
709      extends="net.sf.basedb.clients.web.menu.extensions"
710      >
711      <index>4</index>
712      <about>
713         <name>Hello JSP world</name>
714         <description>
715            This example uses a custom JSP file to display the
716            "Hello world" message instead of a javascript popup.
717         </description>
718      </about>
719      <action-factory>
720         <factory-class>
721            net.sf.basedb.clients.web.extensions.menu.FixedMenuItemFactory
722         </factory-class>
723         <parameters>
724            <title>Hello JSP world!</title>
725            <tooltip>Opens a JSP file with the message</tooltip>
726            <onClick>
727               Main.openPopup('$HOME$/hello_world.jsp?ID=' + getSessionId(), 'HelloJspWorld', 400, 300)
728            </onClick>
729            <icon>~/images/world.png</icon>
730         </parameters>
731      </action-factory>
732   </extension>
733</extensions>
734]]>
735</programlisting>
736   
737    <para>
738      The JAR file should have have the following contents:
739    </para>
740   
741    <programlisting>
742META-INF/extensions.XML
743resources/hello_world.jsp
744resources/images/world.png
745</programlisting>
746   
747    <para>
748      When this extension is installed the <filename>hello_world.jsp</filename>
749      and <filename>world.png</filename> files are automatically extracted
750      to the web servers file system. Each extension is given a unique
751      <code>HOME</code> directory to make sure that extensions doesn't interfere
752      with each other. The URL to the home directory is made available in the
753      <varname>$HOME$</varname> variable. All factory settings that have
754      been annotated with the <interfacename 
755      docapi="net.sf.basedb.util.extensions.xml">VariableSetter</interfacename>
756      will have their values scanned for <code>$HOME$</code> which is replaced
757      with the real URL. It is also possible to use the <varname>$ROOT$</varname>
758      variable to get the root URL for the BASE web application. Never use
759      <code>/base/...</code> since users may install BASE into another
760      path.
761    </para>
762   
763    <para>
764      The tilde (~) in the <sgmltag class="starttag">icon</sgmltag> tag value
765      is also replaced with the <code>HOME</code> path. Note that this kind of
766      replacement is only done on factory settings that have been annotated
767      with the <classname docapi="net.sf.basedb.util.extensions.xml">PathSetter</classname>
768      annotation and is only done on the first character.
769    </para>
770   
771    <note>
772      <para>
773      Unfortunately, the custom JSP file can't use classes that
774      are located in the extension's JAR file. The reason is that the
775      JAR file is not known to Tomcat and Tomcat will not look in the
776      <filename>plugins.dir</filename> folder to
777      try to find classes. There are currently two possible workarounds:
778      </para>
779     
780      <itemizedlist>
781      <listitem>
782        <para>
783        Place classes needed by JSP files in a separate JAR file that
784        is installed into the <filename>WEB-INF/lib</filename> folder.
785        The drawback is that this requires a restart of Tomcat.
786        </para>
787      </listitem>
788     
789      <listitem>
790        <para>
791        Use an X-JSP file instead. This is an experimental feature. See
792        <xref linkend="extensions_developer.resources.xjsp" /> for more
793        information.
794        </para>
795      </listitem>
796      </itemizedlist>
797    </note>
798   
799    <sect2 id="extensions_developer.resources.scripts">
800      <title>Javascript and stylesheets</title>
801     
802      <para>
803        It is possible for an extension to use a custom javascript or stylesheet.
804        However, this doesn't happen automatically and may not be enabled
805        for all extension points. If an extension needs this functionality
806        the action factory or renderer factory must call
807        <methodname>JspContext.addScript()</methodname> or
808        <methodname>JspContext.addStylesheet()</methodname> from the
809        <methodname>prepareContext()</methodname> method.
810      </para>
811     
812      <para>
813        The <classname 
814        docapi="net.sf.basedb.clients.web.extensions">AbstractJspActionFactory</classname>
815        and <classname 
816        docapi="net.sf.basedb.clients.web.extensions">AbstractJspRendererFactory</classname>
817        factory can do this. All factories shipped with BASE extends one of those
818        classes and we recommend that custom-made factories also does this.
819      </para>
820     
821      <para>
822        Factories that are extending one of those two classes can use
823        <sgmltag class="starttag">script</sgmltag> and
824        <sgmltag class="starttag">stylesheet</sgmltag> tags in the
825        <sgmltag class="starttag">parameters</sgmltag> section for an
826        extensions. Each tag may be used more than one time. The
827        values are subject to path and variable substitution.
828      </para>
829     
830      <programlisting language="xml">
831<![CDATA[
832<action-factory>
833   <factory-class>
834      ... some factory class ...
835   </factory-class>
836   <parameters>
837      <script>~/scripts/custom.js</script>
838      <stylesheet>~/css/custom.css</stylesheet>
839      ... other parameters ...
840   </parameters>
841</action-factory>
842]]>
843</programlisting>
844     
845      <para>
846        If scripts and stylesheets has been added to the JSP context
847        the extension system will, <emphasis>in most cases</emphasis>,
848        include the proper HTML to link in the requested scripts and/or
849        stylesheet.
850      </para>
851     
852      <note>
853        <title>Use UTF-8 character encoding</title>
854        <para>
855          The script and stylesheet files should use use UTF-8 character encoding.
856          Otherwise they may not work as expected in BASE.
857        </para>
858      </note>
859     
860      <note>
861        <title>All extension points doesn't support custom scripts/stylesheets</title>
862        <para>
863          In some cases the rendering of the HTML page has gone to far
864          to make is possible to include custom scripts and stylesheets.
865          This is for example the case with the extensions menu. Always
866          check the documentation for the extension point if scripts
867          and stylesheets are supported or not.
868        </para>
869      </note>
870    </sect2>
871   
872    <sect2 id="extensions_developer.resources.xjsp">
873      <title>X-JSP files</title>
874     
875      <para>
876        The drawback with a custom JSP file is that it is not possible to
877        use classes from the extension's JAR file in the JSP code. The reason
878        is that the JAR file is not known to Tomcat and Tomcat will not look
879        in the <filename>plugins.dir</filename> folder to try to find
880        classes.
881      </para>
882     
883      <para>
884        One workaround is to place classes that are needed by the JSP files
885        in a separate JAR file that is placed in <filename>WEB-INF/lib</filename>.
886        The drawback with this is that it requires a restart of Tomcat. It is
887        also a second step that has to be performed manually by the person
888        installing the extension and is maybe forgotten when doing an update.
889      </para>
890     
891      <para>
892        Another workaround is to use an X-JSP file. This is simply a regular
893        JSP file that has a <code>.xjsp</code> extension instead of <code>.jsp</code>.
894        The <code>.xjsp</code> extension will trigger the use of a different
895        compiler that knows how to include the extension's JAR file in the class
896        path.
897      </para>
898     
899      <note>
900        <title>X-JSP is experimental</title>
901        <para>
902          The X-JSP compiler depends on functionality that is internal to
903          Tomcat. The JSP compiler is not part of any open specification
904          and the implementation details may change at any time. This means
905          that the X-JSP compiler may or may not work with future versions
906          of Tomcat. We have currently tested it with Tomcat 6.0.14 only.
907          It will most likely not work with other servlet containers.
908        </para>
909       
910        <para>
911          Adding support for X-JSP requires that a JAR file with the
912          X-JSP compiler is installed into Tomcat's internal <filename>/lib</filename>
913          directory. It is an optional step and not all BASE installations
914          may have the compiler installed. See <xref linkend="plugins.installation.xjspcompiler" />.
915        </para>
916      </note>
917     
918    </sect2>
919  </sect1>
920 
921  <sect1 id="extensions_developer.renderer">
922    <title>Custom renderers and renderer factories</title>
923
924    <para>
925      It is always the responsibility of the extension point to
926      render an action. The need for custom renderers is typically
927      very small, at least if you want your extensions to blend into
928      the look and feel of the BASE web client. Most customizations can
929      be probably be handled by stylesheets and images. That said,
930      you may still have a reason for using a custom renderer.
931    </para>
932   
933    <para>
934      Renderer factories are not very different from action factories.
935      They are specified in the same way in the XML file and uses the
936      same method for initialisation, including support for path
937      conversion, etc. The difference is that you use a
938      <sgmltag class="starttag">renderer-factory</sgmltag> tag
939      instead of an <sgmltag class="starttag">action-factory</sgmltag>
940      tag.
941    </para>
942
943    <programlisting language="xml">
944<![CDATA[
945<renderer-factory>
946   <factory-class>
947      ... some factory class ...
948   </factory-class>
949   <parameters>
950      ... some parameters ...
951   </parameters>
952</renderer-factory>
953]]>
954</programlisting>
955
956    <para>
957      A <interfacename docapi="net.sf.basedb.util.extensions"
958      >RendererFactory</interfacename> also has a <methodname>prepareContext()</methodname>
959      method that can be used to tell the web client about any scripts or stylesheets
960      the extension needs. If your renderer factory extends the <classname 
961        docapi="net.sf.basedb.clients.web.extensions">AbstractJspRendererFactory</classname>
962      class you will not have to worry about this since you can configure
963      scripts and stylesheets in the XML file.
964    </para>
965   
966    <para>
967      A render factory must also implement the <methodname>getRenderer()</methodname> 
968      which should return a <interfacename docapi="net.sf.basedb.util.extensions"
969      >Renderer</interfacename> instance. The extension system will then call
970      the <methodname>Renderer.render()</methodname> method to render an action.
971      This method may be called multiple times if the extension created more than
972      one action.
973    </para>
974   
975    <para>
976      The renderers responsibility is to generate the HTML
977      that is going to be sent to the web client. To do this it needs
978      access to the <classname 
979      docapi="net.sf.basedb.clients.web.extensions">JspContext</classname> 
980      object that was passed to the renderer factory. Here is a simple
981      outline of both a renderer factory and renderer.
982    </para>
983   
984    <programlisting language="java">
985<![CDATA[
986// File: MyRendererFactory.java
987public class MyRendererFactory
988   extends AbstractJspRendererFactory<MyAction>
989{
990
991   public MyRendererFactory()
992   {}
993
994   @Override
995   public MyRenderer getRenderer(InvokationContext context)
996   {
997      return new MyRenderer((JspContext)context.getClientContext());
998   }
999}
1000
1001// File: MyRenderer.java
1002public class MyRenderer
1003   implements Renderer<MyAction>
1004{
1005
1006   private final JspContext context;
1007   public MyRenderer(JspContext context)
1008   {
1009      this.context = context;
1010   }
1011
1012   /**
1013      Generates HTML (unless invisible):
1014      <a class="[clazz]" style="[style]" onclick="[onClick]">[title]</a>
1015   */
1016   public void render(MyAction action)
1017   {
1018      if (!action.isVisible()) return;
1019      Writer out = context.getOut();
1020      try
1021      {
1022         out.write("<a");
1023         if (action.getClazz() != null)
1024         {
1025            out.write(" class=\"" + action.getClazz() + "\"");
1026         }
1027         if (action.getStyle() != null)
1028         {
1029            out.write(" style=\"" + action.getStyle() + "\"");
1030         }
1031         if (action.getOnClick() != null)
1032         {
1033            out.write(" href=\"" + action.getOnClick() + "\"");
1034         }
1035         out.write(">");
1036         out.write(HTML.encodeTags(action.getTitle()));
1037         out.write("</a>\n");
1038      }
1039      catch (IOException ex)
1040      {
1041         throw new RuntimeException(ex);
1042      }
1043   }
1044}
1045]]>
1046</programlisting>
1047  </sect1>
1048 
1049  <sect1 id="extensions_developer.extension_points">
1050    <title>Extension points</title>
1051   
1052    <para>
1053      The BASE web client ships with a number of predefined extension
1054      points. Adding more extension points to the existing web client
1055      requires some minor modifications to the regular JSP files. But this
1056      is not what this chapter is about. This chapter is about defining new
1057      extension points as part of an extension. It is nothing magical about
1058      this and the process is the same as for the regular extension points in
1059      the web client.
1060    </para>
1061   
1062    <para>
1063      The first thing you need to do is to start writing the
1064      XML definition of the extension point. Here is an example
1065      from the web client:
1066    </para>
1067   
1068    <programlisting language="xml">   
1069<![CDATA[
1070<extensions
1071   xmlns="http://base.thep.lu.se/extensions.xsd"
1072   >
1073   <extension-point
1074      id="net.sf.basedb.clients.web.menu.extensions"
1075      >
1076      <action-class>net.sf.basedb.clients.web.extensions.menu.MenuItemAction</action-class>
1077      <name>Menu: extensions</name>
1078      <description>
1079         Extension point for adding extensions to the 'Extensions' menu.
1080         Extensions should provide MenuItemAction instances. The rendering
1081         is internal and extensions can't use their own rendering factories.
1082         The context will only include information about the currently logged
1083         in user, not information about the current page that is displayed.
1084         The reason for this is that the rendered menu is cached as a string
1085         in the user session. The menu is not updated on every page request.
1086         This extension point doesn't support custom stylesheets or javascripts.
1087      </description>
1088   </extension-point>
1089</extensions>
1090]]>
1091</programlisting>
1092   
1093    <para>
1094      The <sgmltag class="starttag">extensions</sgmltag> tag is the root tag and
1095      is needed to set up the namespace and schema validation.
1096    </para>
1097   
1098    <para>
1099      The <sgmltag class="starttag">extension-point</sgmltag> defines a new
1100      extension point. It must have an <sgmltag>id</sgmltag> attribute that
1101      is unique among all installed extension points. We recommend using
1102      the same naming conventions as for java packages. See <ulink 
1103      url="http://www.oracle.com/technetwork/java/codeconventions-135099.html">Java naming
1104      conventions from Oracle</ulink>.
1105    </para>
1106   
1107    <note>
1108      <title>Document the extension point!</title>
1109      <para>
1110        The <sgmltag class="starttag">name</sgmltag> and
1111        <sgmltag class="starttag">description</sgmltag> tags are optional,
1112        but we strongly recommend that values are provided.
1113        The description tag should be used to document the extension
1114        point. Pay special attention to the support (or lack of
1115        support) for custom scripts, stylesheets and renderers.
1116      </para>
1117    </note>
1118   
1119    <para>
1120      The <sgmltag class="starttag">action-class</sgmltag> defines the
1121      interface or class that extensions must provide to the extension
1122      point. This must be a class or interface that is a subclass
1123      of the <interfacename 
1124      docapi="net.sf.basedb.util.extensions">Action</interfacename>
1125      interface. We generally recommend that interfaces are used since
1126      this gives more implementation flexibility for action factories, but
1127      a regular class may work just as well.
1128    </para>
1129     
1130    <para>
1131      The action class is used to carry information about the action,
1132      such as a title, which icon to use, a tooltip text, a javascript
1133      snippet that is invoked on click events, etc. The action class may
1134      be as simple or complex as you like.
1135    </para>
1136     
1137    <note>
1138      <title>Web client extension points</title>
1139      <para>
1140        This is a note for the core developers. Extension points that
1141        are part of the web client should always define the action as
1142        an interface. We recommend that <code>getId()</code>, <code>getClazz()</code>
1143        and <code>getStyle()</code> attributes are always included if this makes
1144        sense. It is usually also a good idea to include <code>isVisible()</code>
1145        and <code>isEnabled()</code> attributes.
1146      </para>
1147    </note>
1148     
1149    <para>
1150      Now, if you are a good citizen you should also provide at least
1151      one implementation of an action factory that can create the
1152      objects of the desired type of action. The factory should
1153      of course be configurable from the XML file.
1154    </para>
1155   
1156    <para>
1157      If you are lazy or if you want to immediately start testing the JSP code
1158      for the extension point, it may be possible to use one of the debugger action
1159      factories in the <package>net.sf.basedb.util.extensions.debug</package>
1160      pacakge.
1161    </para>
1162   
1163    <itemizedlist>
1164    <listitem>
1165      <para>
1166      <classname 
1167      docapi="net.sf.basedb.util.extensions.debug">ProxyActionFactory</classname>:
1168      This action factory can only be used if your action class is an interface
1169      and all important methods starts with <code>get</code> or <code>is</code>.
1170      The proxy action factory uses Java reflection to create a dynamic
1171      proxy class in runtime. It will map all <code>getX()</code> and <code>isY()</code> 
1172      methods to retreive the values from the corresponding parameter in the XML file.
1173      For example, <methodname>getIcon()</methodname> will retrieve the value
1174      of the <sgmltag class="starttag">icon</sgmltag> tag and
1175      <methodname>isVisible()</methodname> from the <sgmltag 
1176      class="starttag">visible</sgmltag>. The factory is smart enough to convert
1177      the string to the correct return value for <type>int</type>, <type>long</type>,
1178      <type>float</type>, <type>double</type> and <type>boolean</type> data types and
1179      their corresponding object wrapper types, if this is needed.
1180      </para>
1181    </listitem>
1182   
1183    <listitem>
1184      <para>
1185      <classname 
1186      docapi="net.sf.basedb.util.extensions.debug">BeanActionFactory</classname>:
1187      This action factory can be used if you have created a bean-like
1188      class that implements the desired action class. The factory will
1189      create an instance of the class specified by the
1190      <sgmltag class="starttag">beanClass</sgmltag> parameter. The factory
1191      will then use Java reflection to find <code>set</code> method
1192      for the other parameters. If there is a parameter <sgmltag class="starttag">icon</sgmltag>
1193      the factory first looks for a <methodname>setIcon(String)</methodname>
1194      method. If it can't find that it will see if there is a <methodname>getIcon()</methodname>
1195      method which has a return type, T. If so, a second attempt is made to find
1196      a <methodname>setIcon(T)</methodname> method. The factory is smart enough
1197      to convert the string value from the XML file to the correct return value
1198       for <type>int</type>, <type>long</type>, <type>float</type>,
1199      <type>double</type> and <type>boolean</type> data types and their
1200      corresponding object wrapper types, if this is needed.
1201      </para>
1202    </listitem>
1203   
1204    </itemizedlist>
1205
1206
1207    <para>
1208      It is finally time to write the JSP code that actually uses the
1209      extension point. It is usually not very complicated. Here is
1210      an exemple which lists snippets from a JSP file:
1211    </para>
1212
1213    <programlisting>
1214<![CDATA[
1215// 1. We recommend using the extensions taglib (and the BASE core taglib)
1216<%@ taglib prefix="ext" uri="/WEB-INF/extensions.tld" %>
1217<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
1218
1219// 2. Prepare the extension point
1220SessionControl sc = Base.getExistingSessionControl(pageContext, true);
1221JspContext jspContext = ExtensionsControl.createContext(sc, pageContext);
1222ExtensionsInvoker invoker = ExtensionsControl.useExtensions(jspContext,
1223   "my.domain.name.extensionspoint");
1224
1225// 3. Output scripts and stylesheets
1226<base:page title="My new extension point">
1227   <base:head>
1228      <ext:scripts context="<%=jspContext%>" />
1229      <ext:stylesheets context="<%=jspContext%>" />
1230   </base:head>
1231   <base:body>
1232   ....
1233
1234// 4a. Using a taglib for rendering with the default renderer
1235<ext:render extensions="<%=invoker%>" context="<%=jspContext%>" />
1236
1237// 4b. Or, use the iterator and a more hard-coded approach
1238<%
1239Iterator it = invoker.iterate();
1240while (it.hasNext())
1241{
1242   MyAction action = (MyAction)it.next();
1243   String html = action.getTitle() +
1244    ....
1245   out.write(html);
1246}
1247%>
1248]]>
1249</programlisting>
1250
1251    <sect2 id="extensions_developer.error_handler">
1252      <title>Error handlers</title>
1253     
1254      <para>
1255        An extension points may define a custom error handler. If not, the
1256        default error handler is used which simply writes a message to the
1257        log file. If you want to use a different error handler, create
1258        a <sgmltag class="starttag">error-handler-factory</sgmltag> tag
1259        inside the extension point definition. The <sgmltag 
1260        class="starttag">factory-class</sgmltag> is a required subtag and
1261        must specify a class with a public no-argument constructor that
1262        implements the
1263        <interfacename docapi="net.sf.basedb.util.extensions">ErrorHandlerFactory</interfacename>
1264        interface. The <sgmltag class="starttag">parameters</sgmltag> subtag is
1265        optional and can be used to specify initialization parameters for the
1266        factory just as for action and renderer factories.
1267      </para>
1268
1269      <programlisting language="xml">   
1270<![CDATA[
1271<extensions
1272   xmlns="http://base.thep.lu.se/extensions.xsd"
1273   >
1274   <extension-point
1275      id="net.sf.basedb.clients.web.menu.extensions"
1276      >
1277      <action-class>net.sf.basedb.clients.web.extensions.menu.MenuItemAction</action-class>
1278      <name>Menu: extensions</name>
1279      <error-handler-factory>
1280         <factory-class>
1281            ... some factory class ...
1282         </factory-class>
1283         <parameters>
1284            ... initialization parameters ...
1285         </parameters>
1286      </error-handler-factory>
1287   </extension-point>
1288</extensions>
1289]]>
1290</programlisting>
1291    </sect2>
1292  </sect1>
1293 
1294  <sect1 id="extensions_developer.servlets">
1295    <title>Custom servlets</title>
1296    <para>
1297      It is possible for an extension to include servlets without having
1298      to register those servlets in Tomcat's <filename>WEB-INF/web.xml</filename>
1299      file. The extension needs to be in a JAR file as usual. The servlet class
1300      should be located in the JAR file following regular Java conventions.
1301      Eg. The class <classname>my.domain.ServletClass</classname> should
1302      be located at <filename>my/domain/ServletClass.class</filename>. You
1303      also need to create a second XML file that contains the servlet
1304      definitions at <filename>META-INF/servlets.xml</filename>. The format for
1305      defining servlets in this file is very similar to how servlets are
1306      defined in the <filename>web.xml</filename> file. Here is an example:
1307    </para>
1308   
1309    <programlisting language="xml">
1310<![CDATA[
1311<?xml version="1.0" encoding="UTF-8" ?>
1312<servlets xmlns="http://base.thep.lu.se/servlets.xsd">
1313   <servlet>
1314      <servlet-name>HelloWorld</servlet-name>
1315      <servlet-class>net.sf.basedb.examples.extensions.HelloWorldServlet</servlet-class>
1316      <init-param>
1317         <param-name>template</param-name>
1318         <param-value>Hello {user}! Welcome to the Servlet world!</param-value>
1319      </init-param>
1320   </servlet>
1321</servlets>
1322]]>
1323</programlisting>
1324
1325    <para>
1326      The <sgmltag class="starttag">servlets</sgmltag> tag is the root tag and is
1327      needed to set up the namespace and schema validation. This may contain
1328      any number of <sgmltag class="starttag">servlet</sgmltag> tags, each one
1329      defining a single servlet.
1330    </para>
1331   
1332    <para>
1333      The <sgmltag class="starttag">servlet-name</sgmltag> tag contains the name
1334      of the servlet. This is a required tag and must be unique among the servlets
1335      defined by this extension. Other extensions may use the same name without any
1336      problems.
1337    </para>
1338   
1339    <para>
1340      The <sgmltag class="starttag">servlet-class</sgmltag> tag contains the name
1341      of implementing class. This is required and the class must implement
1342      the <interfacename>Servlet</interfacename> interface and have a public,
1343      no-argument constructor. We recommend that servlet implementations instead
1344      extends the <classname>HttpServlet</classname> class. This will make the
1345      servlet programming easier.
1346    </para>
1347   
1348    <para>
1349      A servlet may have any number <sgmltag class="starttag">init-param</sgmltag>
1350      tags, containing initialisation parameters for the servlet. Here is the
1351      code for the servlet references in the above example.
1352    </para>
1353
1354    <programlisting language="java">
1355<![CDATA[
1356public class HelloWorldServlet
1357   extends HttpServlet
1358{
1359   private String template;
1360   public HelloWorldServlet()
1361   {}
1362
1363   @Override
1364   public void init()
1365      throws ServletException
1366   {
1367      ServletConfig cfg = getServletConfig();
1368      template = cfg.getInitParameter("template");
1369      if (template == null) template = "Hello {user}.";
1370   }
1371
1372   @Override
1373   protected void doGet(HttpServletRequest request, HttpServletResponse response)
1374      throws ServletException, IOException
1375   {
1376      final SessionControl sc = Base.getExistingSessionControl(request, true);
1377      final DbControl dc = sc.newDbControl();
1378      try
1379      {
1380         User current = User.getById(dc, sc.getLoggedInUserId());
1381         PrintWriter out = response.getWriter();
1382         out.print(template.replace("{user}", current.getName()));
1383      }
1384      finally
1385      {
1386         if (dc != null) dc.close();
1387      }
1388   }
1389   @Override
1390   protected void doPost(HttpServletRequest req, HttpServletResponse resp)
1391      throws ServletException, IOException
1392   {
1393      doGet(req, resp);
1394   }
1395}
1396]]>
1397</programlisting>
1398
1399    <para>
1400      Invoking the servlet is done with a URL that is constructed like:
1401      <filename>$HOME$/[servlet-name].servlet</filename>, where <code>$HOME$</code>
1402      is the home directory of the extension. An alternate URL
1403      that doesn't require the <filename>.servlet</filename> extension is available:
1404      <filename>$SERVLET_HOME$/[servlet-name]</filename>, where <code>$SERVLET_HOME$</code>
1405      is the home directory of servlets for the extension. Note that this
1406      directory is on a different sub-path than the <code>$HOME$</code> directory.
1407    </para>
1408    <para>
1409      Extra path information is supported
1410      if it is inserted between the servlet name and the <filename>.servlet</filename>
1411      extension: <filename>$HOME$/[servlet-name][/extra/path/info].servlet</filename>,
1412      <filename>$SERVLET_HOME$/[servlet-name][/extra/path/info]</filename>
1413     
1414    </para>
1415    <para>
1416      Query parameters are supported as normal:
1417      <filename>$HOME$/[servlet-name].servlet?param1=value&amp;param2=value</filename>,
1418      <filename>$SERVLET_HOME$/[servlet-name]?param1=value&amp;param2=value</filename>
1419    </para>
1420   
1421
1422    <programlisting language="xml">
1423<![CDATA[
1424<extension
1425   id="net.sf.basedb.clients.web.menu.extensions.helloservletworld"
1426   extends="net.sf.basedb.clients.web.menu.extensions"
1427   >
1428   <index>5</index>
1429   <about>
1430      <name>Hello Servlet world</name>
1431      <description>
1432         This example uses a custom Servlet page to display the
1433         "Hello world" message instead of a javascript popup.
1434      </description>
1435   </about>
1436   <action-factory>
1437      <factory-class>
1438         net.sf.basedb.clients.web.extensions.menu.FixedMenuItemFactory
1439      </factory-class>
1440      <parameters>
1441         <title>Hello Servlet world!</title>
1442         <tooltip>Opens a Servlet generated page with the message</tooltip>
1443         <onClick>
1444           Main.openPopup('$HOME$/HelloWorld.servlet?ID=' + getSessionId(), 'HelloServletWorld', 400, 300)
1445         </onClick>
1446         <icon>~/images/servlet.png</icon>
1447      </parameters>
1448   </action-factory>
1449</extension>
1450]]>
1451</programlisting>
1452   
1453    <note>
1454      <para>
1455        To keep things as simple as possible, a new instance of the servlet
1456        class is created for each request. If the servlet needs complex or
1457        expensive initialisation, that should be externalised to other
1458        classes that the servlet can use.
1459      </para>
1460    </note>
1461   
1462  </sect1>
1463 
1464  <sect1 id="extensions_developer.base_extension_points">
1465    <title>Extension points defined by BASE</title>
1466   
1467    <para>
1468      In this section, we will give an overview of the extension points defined
1469      by BASE. Most extension points are used in the web client to add
1470      buttons and menu items, but there are a few for the core API as well.
1471    </para>
1472   
1473    <sect2 id="extensions_developer.menuitems">
1474      <title>Menu: extensions</title>
1475     
1476      <para>
1477        Menu items can be added to the top-level
1478        <menuchoice><guimenu>Extensions</guimenu></menuchoice>
1479        menu. Actions should implement the interface: <interfacename 
1480        docapi="net.sf.basedb.clients.web.extensions.menu">MenuItemAction</interfacename>
1481      </para>
1482     
1483      <para>
1484        The <methodname>MenuItemAction.getMenuType()</methodname> provides support
1485        for <constant>MENUITEM</constant>, <constant>SUBMENU</constant> and
1486        <constant>SEPARATOR</constant> menus. Which of the other properties that
1487        are needed depend on the menu type. Read the javadoc for more information.
1488        This extension point doesn't support custom javascripts or stylesheets and
1489        the rendering is internal (eg. extensions can't provide their own renderers).
1490      </para>
1491     
1492      <para>
1493        BASE ships with two action factories:
1494        <classname docapi="net.sf.basedb.clients.web.extensions.menu">FixedMenuItemFactory</classname> and
1495        <classname docapi="net.sf.basedb.clients.web.extensions.menu">PermissionMenuItemFactory</classname>.
1496       
1497        The fixed factory provides a menu that is the same for all users.
1498        The permission factory can disable or hide a menu depending on the
1499        logged in user's role-based permissions. The title, icon, etc. can have
1500        different values depending on if the menu item is disabled or enabled.
1501      </para>
1502    </sect2>
1503   
1504    <sect2 id="extensions_developer.toolbars">
1505      <title>Toolbars</title>
1506      <para>
1507        Most toolbars on all list and single-item view pages can
1508        be extended with extra buttons. Actions should implement
1509        the interface: <interfacename 
1510          docapi="net.sf.basedb.clients.web.extensions.toolbar">ButtonAction</interfacename>.
1511        Button actions are very simple and only need to provide things like
1512        a title, tooltip, on-click script, etc. This extension point has
1513        support for custom javascript, stylesheets and renderers. The
1514        default renderer is <classname 
1515        docapi="net.sf.basedb.clients.web.extensions.toolbar">ToolbarButtonRendererFactory</classname>.
1516      </para>
1517
1518      <para>
1519        BASE ships with two action factories:
1520        <classname docapi="net.sf.basedb.clients.web.extensions.toolbar">FixedButtonFactory</classname> and
1521        <classname docapi="net.sf.basedb.clients.web.extensions.toolbar">PermissionButtonFactory</classname>.
1522       
1523        The fixed factory provides a toolbar button that is the same for all users.
1524        The permission factory can disable or hide a buton depending on the
1525        logged in user's role-based permissions. The title, icon, etc. can have
1526        different values depending on if the menu item is disabled or enabled.
1527      </para>
1528    </sect2>
1529
1530    <sect2 id="extensions_developer.edit_dialogs">
1531      <title>Edit dialogs</title>
1532     
1533      <para>
1534        Most item edit dialogs can be extended with additional tabs.
1535        Actions should implement the interface: <interfacename 
1536          docapi="net.sf.basedb.clients.web.extensions.tabcontrol">TabAction</interfacename>.
1537        The actions are, in principle, simple and only need to
1538        provide a title and content (HTML). The action may also provide
1539        javascripts for validation, etc. This extension point has support
1540        for custom stylesheets and javascript. Rendering is fixed and can't
1541        be overridden.
1542      </para>
1543     
1544      <para>
1545        BASE ships with two action factories:
1546        <classname docapi="net.sf.basedb.clients.web.extensions.tabcontrol">FixedTabFactory</classname> and
1547        <classname docapi="net.sf.basedb.clients.web.extensions.tabcontrol">IncludeContentTabFactory</classname>.
1548       
1549        The fixed factory provides a tab with fixed content that same for all users
1550        and the same for all items. This factory is not very useful in a real scenario.
1551        The other factory provides content by including the output from another resource,
1552        eg. a JSP page, a servlet, etc. The current context is stored in a request-scoped
1553        attribute under the key given by <code>JspContext.ATTRIBUTE_KEY</code>.
1554        A JSP or servlet should use this to hook into the current flow. Here is a code example:
1555      </para>
1556     
1557      <programlisting language="java">
1558<![CDATA[
1559// Get the JspContext that was created on the main edit page
1560final JspContext jspContext = (JspContext)request.getAttribute(JspContext.ATTRIBUTE_KEY);
1561
1562// The current item is found in the context. NOTE! Can be null if a new item
1563final BasicItem item = (BasicItem)jspContext.getCurrentItem();
1564
1565// Get the DbControl and SessionControl used to handle the request (do not close!)
1566final DbControl dc = jspContext.getDbControl();
1567final SessionControl sc = dc.getSessionControl();
1568]]>
1569</programlisting>
1570
1571      <para>
1572        The extra tab need to be paired with an extensions that is invoked when
1573        the edit form is saved. Each edit-dialog extension point has a corresponding
1574        on-save extension point. Actions should implement the interface: <interfacename 
1575          docapi="net.sf.basedb.clients.web.extensions.edit">OnSaveAction</interfacename>.
1576       
1577        This interface define three callback methods that BASE will call when
1578        saving an item. The <methodname>OnSaveAction.onSave()</methodname>
1579        method is called first, but not until all regular properties have been
1580        updated. If the transaction is committed the <methodname>OnSaveAction.onCommit()</methodname>
1581        method is also called, otherwise the <methodname>OnSaveAction.onRollback()</methodname>
1582        is called. The <methodname>onSave()</methodname> method can throw an exception that
1583        will be displayed to the user. The other callback method should not throw exceptions
1584        at all, since that may result in undefined behaviour and can be confusing for the user.
1585      </para>
1586    </sect2>
1587   
1588    <sect2 id="extensions_developer.bioassayset_tools">
1589      <title>Bioassay set: Tools</title>
1590      <para>
1591        The bioassay set listing for an experiment has a <guilabel>Tools</guilabel>
1592        column which can be extended by extensions. This extension point is similar
1593        to the toolbar extension points and actions should implement
1594        the interface: <interfacename 
1595          docapi="net.sf.basedb.clients.web.extensions.toolbar">ButtonAction</interfacename>.
1596      </para>
1597     
1598      <para>
1599        Note that the list can contain
1600        <classname docapi="net.sf.basedb.core">BioAssaySet</classname>,
1601        <classname docapi="net.sf.basedb.core">Transformation</classname> and
1602        <classname docapi="net.sf.basedb.core">ExtraValue</classname> items.
1603        The factory implementation need to be aware of this if it uses
1604        the <methodname>JspContext.getItem()</methodname> method to examine the
1605        current item.
1606      </para>
1607    </sect2>
1608
1609    <sect2 id="extensions_developer.bioassayset_plots">
1610      <title>Bioassay set: Overview plots</title>
1611      <para>
1612        The bioassay set page has a tab <guilabel>Overview plots</guilabel>.
1613        The contents of this tab is supposed to be some kind of images
1614        that have been generated from the data in the current bioassay set.
1615        What kind of plots that can be generated typically depends on the
1616        kind of data you have. BASE ships with an extension
1617        (<classname docapi="net.sf.basedb.clients.web.extensions">MAPlotFactory</classname>)
1618        that creates MA plots and Correction factor plots for 2-channel bioassays.
1619        Actions should implement the interface:
1620        <interfacename docapi="net.sf.basedb.clients.web.extensions.plot">OverviewPlotAction</interfacename>.
1621        A single action generates a sub-tab in the <guilabel>Overview plots</guilabel> tab.
1622        The sub-tab may contain one or more images. Each image is defined by a
1623        <interfacename docapi="net.sf.basedb.clients.web.extensions.plot">PlotGenerator</interfacename>
1624        which sets the size of the image and provides an URL to a servlet that generates
1625        the actual image. It is recommended that the servlet cache images since the
1626        data in a bioassay set never changes. The BASE core API provides a
1627        system-managed file cache that is suitable for this. Call
1628        <methodname>Application.getStaticCache()</methodname> to get a
1629        <classname docapi="net.sf.basedb.util">StaticCache</classname> instance.
1630        See the source code for the core
1631        <classname docapi="net.sf.basedb.clients.web.servlet">PlotServlet</classname>
1632        for details of how to use the cache.
1633      </para>
1634     
1635    </sect2>
1636
1637    <sect2 id="extensions_developer.services">
1638      <title>Services</title>
1639      <para>
1640        A service is a piece of code that is loaded when the BASE web
1641        server starts up. The service is then running as long as the BASE web
1642        server is running. It is possible to manually stop and start services.
1643        This extension point is different from most others in that it doesn't
1644        affects the visible interface. Since services are loaded at startup time,
1645        this also means that the context passed to ActionFactory methods will
1646        not have any <classname>ClientContext</classname> associated with it
1647        (eg. the <methodname>InvokationContext.getClientContext()</methodname>
1648        always return null). There is also no meaning for extensions to specify
1649        a <interfacename>RendererFactory</interfacename>. Service actions should
1650        implement the interface:
1651        <interfacename docapi="net.sf.basedb.clients.web.extensions.service">ServiceControllerAction</interfacename>.
1652       
1653        The interface provides <methodname>start()</methodname> and
1654        <methodname>stop()</methodname> methods for controlling the service.
1655        BASE doesn't ship with any service, but there is an FTP service
1656        available at the BASE plug-ins site: <ulink 
1657        url="http://baseplugins.thep.lu.se/wiki/net.sf.basedb.ftp">
1658        http://baseplugins.thep.lu.se/wiki/net.sf.basedb.ftp</ulink>
1659      </para>
1660     
1661    </sect2>
1662
1663    <sect2 id="extensions_developer.connection_manager">
1664      <title>Connection managers</title>
1665      <para>
1666        This extension point adds support for using external files
1667        in BASE. This is a core extension point and is available
1668        independently of the web client. Actions should implement
1669        the interface:
1670        <interfacename docapi="net.sf.basedb.util.uri">ConnectionManagerFactory</interfacename>.
1671      </para>
1672     
1673      <para>
1674        The <methodname>getDisplayName()</methodname> and <methodname>getDescription()</methodname>
1675        methods are used in the gui when a user manually selects which connection
1676        manager to use. The <methodname>supports(URI)</methodname> is used when auto-selecting
1677        a connection manager based on the URI of the resource.
1678      </para>
1679     
1680      <para>
1681        BASE ships with factory that supports HTTP and HTTPS file
1682        references: <classname 
1683        docapi="net.sf.basedb.util.uri.http">HttpConnectionManagerFactory</classname>.
1684       
1685        The BASE plug-ins site has an connection manager that support
1686        the Hadoop distributed file system (HDFS): <ulink 
1687        url="http://baseplugins.thep.lu.se/wiki/net.sf.basedb.hdfs">
1688        http://baseplugins.thep.lu.se/wiki/net.sf.basedb.hdfs</ulink>
1689      </para>
1690     
1691    </sect2>
1692
1693    <sect2 id="extensions_developer.fileset_validator">
1694      <title>Fileset validators</title>
1695     
1696      <itemizedlist>
1697        <title>See also</title>
1698        <listitem><para><xref linkend="core_api.data_in_files" /></para></listitem>
1699        <listitem><para><xref linkend="data_api.platforms" /></para></listitem>
1700      </itemizedlist>
1701 
1702      <para>
1703        In those cases where files are used to store data instead
1704        of importing it to the database, BASE can use extensions to
1705        check that the supplied files are valid and also to extract
1706        metadata from the files. For example, the
1707        <classname docapi="net.sf.basedb.util.affymetrix">CelValidationAction</classname>
1708        is used to check if a file is a valid Affymetrix CEL file and
1709        to extract data headers and the number of spots from it.
1710      </para>
1711     
1712      <para>
1713        Validation and metadata extraction actions should implement
1714        the <interfacename docapi="net.sf.basedb.util.fileset">ValidationAction</interfacename>
1715        interface. This is a core extension point and is available
1716        independently of the web client.
1717      </para>
1718     
1719      <para>
1720        This extension point is a bit more complex than most other extension
1721        points. To begin with, the factory class will be called with the
1722        owner of the file set as the current item. Eg. the
1723        <code>ClientContext.getCurrentItem()</code> should return a
1724        <interfacename docapi="net.sf.basedb.core">FileStoreEnabled</interfacename>
1725        item. It is recommended that the factory performs a pre-filtering
1726        of the items to avoid calling the actual validation code on unsupported
1727        files. For example, the <classname docapi="net.sf.basedb.util.affymetrix">CelValidationFactory</classname>
1728        will check that the item is a <classname docapi="net.sf.basedb.core">RawBioAssay</classname>
1729        item using the Affymetrix platform.
1730      </para>
1731     
1732      <para>
1733        Each file in the file set is then passed to the
1734        <methodname>ValidationAction.acceptFile(FileSetMember)</methodname>
1735        which may accept or reject the file. If the file is accepted it may
1736        be accepted for immediate validation or later validation. The latter option
1737        is useful in more complex scenarios were files need to be validated as a group.
1738        If the file is accepted the <methodname>ValidationAction.validateAndExtractMetadata()</methodname>
1739        is called, which is were the real work should happen.
1740      </para>
1741     
1742      <para>
1743        The extensions for this extension point is also called when a file is replaced
1744        or removed from the file set. The calling sequence to set up the validation is
1745        more or less the same as described above, but the last step is to
1746        call <methodname>ValidationAction.resetMetadata()</methodname> instead
1747        of <methodname>ValidationAction.validateAndExtractMetadata()</methodname>.
1748      </para>
1749
1750      <tip>
1751        <title>
1752          Use the <classname docapi="net.sf.basedb.util.fileset">SingleFileValidationAction</classname>
1753          class.
1754        </title>
1755        <para>
1756          Most validators that work on a single file at a time may find the
1757          <classname>SingleFileValidationAction</classname> class useful. Is should
1758          simplify the task of making sure that only the desired file type is
1759          validated. See the source code of the <classname 
1760          docapi="net.sf.basedb.util.affymetrix">CelValidationAction</classname>
1761          class for an example.
1762        </para>
1763      </tip>
1764     
1765    </sect2>
1766   
1767  </sect1>
1768 
1769</chapter>
Note: See TracBrowser for help on using the repository browser.