source: trunk/doc/src/docbook/developer/plugins.xml @ 5782

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

References #1590: Documentation cleanup

Restructured documentation to generate shorter filenames.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 168.5 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: plugins.xml 5782 2011-10-04 13:43:16Z nicklas $:
7 
8  Copyright (C) 2007 Johan Enell, Peter Johansson, Nicklas Nordborg, Martin Svensson
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="plugin_developer">
28  <?dbhtml dir="plugins" filename="index.html" ?>
29  <title>Plug-in developer</title>
30  <sect1 id="plugin_developer.organize">
31    <?dbhtml filename="organize.html" ?>
32    <title>How to organize your plug-in project</title>
33   
34    <sect2 id="plugin_developer.organize.ant">
35      <title>Using Ant</title>
36      <para>
37        Here is a simple example of how you might organize your project using ant
38        (<ulink url="http://ant.apache.org">http://ant.apache.org</ulink>) as the build
39        tool. This is just a recommendation that we have found to be working
40        well. You may choose to do it another way.
41      </para>
42
43      <sect3 id="plugin_developer.organize.ant.directories">
44        <title>Directory layout</title>
45        <para>
46       
47          Create a directory on your computer where you want
48          to store your plug-in project. This directory is the
49          <filename class="directory"><replaceable>pluginname</replaceable>/</filename>
50          directory in the listing below. You should also create some subdirectories:
51        </para>
52       
53        <literallayout>
54<filename class="directory"><replaceable>pluginname</replaceable>/</filename>
55<filename class="directory"><replaceable>pluginname</replaceable>/bin/</filename>
56<filename class="directory"><replaceable>pluginname</replaceable>/lib/</filename>
57<filename class="directory"><replaceable>pluginname</replaceable>/src/<replaceable>org/company</replaceable>/</filename>
58<filename class="directory"><replaceable>pluginname</replaceable>/META-INF/</filename>
59<filename class="directory"><replaceable>pluginname</replaceable>/META-INF/lib/</filename>
60        </literallayout>
61        <para>
62          The <filename class="directory">bin/</filename>
63          directory is empty to start with. It will contain the compiled code.
64          In the <filename class="directory">lib/</filename>
65          directory you should put <filename>base-core-3.x.x.jar</filename>
66          and other library files that is needed when compiling the source code,
67          but doesn't have to be distributed with your plug-in (since they are already
68          included in the BASE distribution). In the <filename class="directory">META-INF/lib/</filename>
69          directory you should put other library files that are needed both when compiling
70          and when distributing your plug-in.
71        </para>
72        <para>
73          The <filename class="directory">src/</filename>
74          directory contains your source code. In this directory you should create
75          subdirectories corresponding to the package name of your plug-in
76          class(es). See <ulink url="http://en.wikipedia.org/wiki/Java_package"
77          >http://en.wikipedia.org/wiki/Java_package</ulink> for information
78          about conventions for naming packages. The
79          <filename class="directory">META-INF/</filename> directory contains metadata
80          about the plug-in and are needed for easy installation. Typically you'll
81          always need <filename>MANIFEST.MF</filename> and <filename>extensions.xml</filename>
82          but, depending on what you are developing, other files are also needed.
83        </para>
84      </sect3>
85   
86      <sect3 id="plugin_developer.organize.ant.buildfile">
87        <title>The build file</title>
88        <para>
89          In the root of your directory, create the build file:
90          <filename>build.xml</filename>.
91          Here is an example that will compile your plug-in and put it in a JAR file.
92        </para>
93        <example id="plugin_developer.organize.build.file">
94          <title>A simple build file</title>
95          <programlisting language="xml">
96<![CDATA[
97<?xml version="1.0" encoding="UTF-8"?>
98<project
99   name="MyPlugin"
100   default="build.plugin"
101   basedir="."
102   >
103
104   <!-- variables used -->
105   <property name="plugin.name" value="MyPlugin" />
106   <property name="src" value="src" />
107   <property name="bin" value="bin" />
108
109   <!-- set up classpath for compiling -->
110   <path id="classpath">
111      <fileset dir="lib">
112         <include name="**/*.jar"/>
113      </fileset>
114      <fileset dir="META-INF/lib">
115         <include name="**/*.jar"/>
116      </fileset>
117   </path>
118
119   <!-- main target -->
120   <target
121      name="build.plugin" 
122      description="Compiles the plug-in and put in jar"
123      >
124      <javac
125         encoding="UTF-8"
126         srcdir="${src}"
127         destdir="${bin}"
128         classpathref="classpath">
129      </javac>
130      <jar
131         jarfile="${plugin.name}.jar"
132         manifest="META-INF/MANIFEST.MF"
133         >
134         
135         <!-- compiled source code -->
136         <fileset dir="${bin}" />
137         
138         <!-- metadata and extra JAR files -->
139         <fileset dir="." includes="META-INF/**" />
140      </jar>
141    </target>
142</project>
143]]>
144</programlisting>
145        </example>
146        <para>
147          If your plug-in depends JAR files not included with the standard
148          BASE installation, you must create a file called
149          <filename>MANIFEST.MF</filename> in the project <filename>META-INF</filename>
150          directory. List the other JAR files as in the following example.
151          If your plug-in does not depend on other JAR files, you may remove the
152          <sgmltag class="attribute">manifest</sgmltag> 
153          attribute of the <sgmltag class="starttag">jar</sgmltag> tag.
154<programlisting>Manifest-Version: 1.0
155Class-Path: lib/OtherJar.jar lib/ASecondJar.jar</programlisting>
156        See also <xref linkend="plugin_developer.classload" /> for more information
157        regarding class loading when a plug-in depends on a external JAR files.
158        </para>
159       
160        <para>
161          To make installation of your plug-in easier it is a good idea to include
162          the <filename>META-INF/extensions.xml</filename>. This file should
163          include information about your plug-in, such as the name, author, version,
164          and the main Java class for the plug-in. See
165          <xref linkend="plugin_developer.organize.autoinstallcompatible" />
166          for get more information about this feature.
167        </para>
168      </sect3>
169       
170      <sect3 id="plugin_developer.organize.ant.build">
171        <title>Building the plug-in</title>
172        <para>
173          Compile the plug-in simply by typing
174          <command>ant</command>
175          in the console window. If all went well the
176          <filename>MyPlugin.jar</filename>
177          will be created in the same directory.
178        </para>
179        <para>
180          To install the plug-in copy the JAR file to the server's plug-in directory.
181          For more information about the actual installation read
182          <xref linkend="plugins.installation" />.
183        </para>
184      </sect3>
185    </sect2>
186   
187    <sect2 id="plugin_developer.organize.autoinstallcompatible">
188      <title>Make the plug-in compatible with the auto-installation wizard</title>
189      <para>
190        BASE has support for automatically detecting new plug-ins with
191        the auto-installation wizard. The wizard makes it very easy for a
192        server administrator to install new plug-ins. See
193        <xref linkend="plugins.installation.wizard" />.
194      </para>
195     
196      <para>
197        The auto-install feature requires that a plug-in provides some information
198        about itself. The wizard looks in all JAR file for the file
199        <filename>META-INF/extensions.xml</filename>. This file contains
200        some information about the plug-in(s). Here is an example:
201      </para>
202 
203      <programlisting language="xml">
204<![CDATA[
205<?xml version="1.0" encoding="UTF-8" ?>
206<extensions xmlns="http://base.thep.lu.se/extensions.xsd">
207  <!-- information about the complete package (which may contain many plug-in definitions) -->
208  <about>
209    <name>My plug-in package</name>
210    <description>
211      This package contains some very useful plug-ins...
212    </description>
213    <version>1.3</version>
214    <min-base-version>3.0</min-base-version>
215    <copyright>You</copyright>
216    <url>http://www.company.org/with/more/info/about/plugins</url>
217    <email>some.email.address@company.org</email>
218  </about>
219 
220  <!-- Defines a single plug-in (can be repeated multiple times) -->
221  <plugin-definition id="PluginX">
222    <about>
223      <name>Plug-in X</name>
224      <description>
225        Calculates the X transform of....
226      </description>
227    </about>
228    <plugin-class>org.company.PluginClassX</plugin-class>
229  </plugin-definition>
230</extensions>
231]]>
232</programlisting>
233      <para>
234        The first two lines should be the same in all <filename>extensions.xml</filename> files.
235        The rest of the tags are described in following list.
236        <variablelist>
237          <varlistentry>
238            <term><sgmltag class="starttag">about</sgmltag></term>
239            <listitem>
240              <para>
241                The first <sgmltag class="starttag">about</sgmltag> tag is information
242                about the package as a whole. We recommend putting in as much information
243                as possible here. Supported sub-tags are:
244               
245                <sgmltag class="starttag">name</sgmltag>, <sgmltag class="starttag">description</sgmltag>,
246                <sgmltag class="starttag">version</sgmltag>, <sgmltag class="starttag">min-base-version</sgmltag>,
247                <sgmltag class="starttag">max-base-version</sgmltag>, <sgmltag class="starttag">contact</sgmltag>,
248                <sgmltag class="starttag">email</sgmltag>, <sgmltag class="starttag">url</sgmltag> and
249                <sgmltag class="starttag">copyright</sgmltag>
250              </para>
251              <para>
252                Each plug-in that is defined in this file may have it's <sgmltag class="starttag">about</sgmltag>
253                tag which override the global information. Typically, you'll only overide the name and
254                description tags.
255              </para>
256            </listitem>
257          </varlistentry>
258          <varlistentry>
259            <term><sgmltag class="starttag">plugin-definition</sgmltag></term>
260            <listitem>
261              <para>
262                This is the main element for defining a plug-in. It can override the global
263                <sgmltag class="starttag">about</sgmltag> information. We recommend that at
264                least a <sgmltag class="starttag">name</sgmltag> and <sgmltag class="starttag">description</sgmltag>
265                is provided.               
266              </para>
267            </listitem>
268          </varlistentry>
269          <varlistentry>
270            <term>
271              <sgmltag class="starttag">plugin-class</sgmltag>
272            </term>
273            <listitem>
274              <para>
275                The value is the fill class name of the plug-in's main
276                class.
277              </para>
278            </listitem>
279          </varlistentry>
280          <varlistentry>
281            <term>
282              <sgmltag class="starttag">min-base-version</sgmltag>
283              <sgmltag class="starttag">max-base-version</sgmltag>
284            </term>
285            <listitem>
286              <para>
287                Optional sub-tags in the <sgmltag class="starttag">about</sgmltag> section.
288                Values are two- or three-digits separated by a dot. For example,
289                3.0.0 and 3.1. If values are given the plug-in will only be installed
290                if the server's BASE version is within the bounds.
291              </para>
292            </listitem>
293          </varlistentry>
294        </variablelist>
295      </para>
296     
297      <sect3 id="plugin_developer.organize.autoinstallcompatible.configurations">
298        <title>Installing plug-in configurations</title>
299     
300        <para>
301          The installation wizard has support for installing plug-in configurations.
302          If some of your plug-ins has support for different configurations that you want
303          to ship as part of the package, use the "Plug-in configuration exporter" in BASE
304          and include the generated file in <filename>META-INF/plugin-configurations.xml</filename>
305          inside your JAR file.
306        </para>
307     
308      </sect3>
309    </sect2>
310   
311  </sect1>
312
313  <sect1 id="plugin_developer.api">
314    <?dbhtml filename="api.html" ?>
315    <title>The Plug-in API</title>
316    <para>
317    </para>
318   
319    <sect2 id="plugin_developer.api.interfaces">
320   
321      <title>The main plug-in interfaces</title>
322      <para>
323        The BASE API defines two interfaces and one
324        abstract class that are vital for implementing plug-ins:
325        <itemizedlist spacing="compact">
326          <listitem>
327            <simpara>
328              <interfacename docapi="net.sf.basedb.core.plugin">net.sf.basedb.core.plugin.Plugin</interfacename>
329            </simpara>
330          </listitem>
331          <listitem>
332            <simpara>
333              <interfacename docapi="net.sf.basedb.core.plugin">net.sf.basedb.core.plugin.InteractivePlugin</interfacename>
334            </simpara>
335          </listitem>
336          <listitem>
337            <simpara>
338              <classname docapi="net.sf.basedb.core.plugin">net.sf.basedb.core.plugin.AbstractPlugin</classname>
339            </simpara>
340          </listitem>
341        </itemizedlist>
342       
343        A plug-in must always implement the
344        <interfacename docapi="net.sf.basedb.core.plugin">Plugin</interfacename> interface.
345        The <interfacename docapi="net.sf.basedb.core.plugin">InteractivePlugin</interfacename>
346        interface is optional, and is only needed if you want user interaction.
347        The <classname docapi="net.sf.basedb.core.plugin">AbstractPlugin</classname> is a useful base class
348        that your plug-in can use as a superclass. It provides default implementations
349        for some of the interface methods and also has utility methods for
350        validating and storing job and configuration parameter values. Another
351        reason to use this class as a superclass is that it will shield your
352        plug-in from future changes to the Plug-in API. For example, if
353        we decide that a new method is needed in the <interfacename docapi="net.sf.basedb.core.plugin">Plugin</interfacename> 
354        interface we will also try to add a default implementation in
355        the <classname docapi="net.sf.basedb.core.plugin">AbstractPlugin</classname> class.
356      </para>
357 
358      <important>
359        <para>
360        A plug-in must also have public no-argument contructor. Otherwise, BASE will not
361        be able to create new instances of the plug-in class.
362        </para>
363      </important>
364     
365      <sect3 id="plugin_developer.api.interfaces.plugin">
366        <title>The net.sf.basedb.core.plugin.Plugin interface</title>
367        <para>
368          This interface defines the following methods and must be implemented
369          by all plug-ins.
370        </para>
371        <variablelist>
372          <varlistentry>
373            <term>
374              <methodsynopsis language="java">
375                <modifier>public</modifier>
376                <type>Plugin.MainType</type>
377                <methodname>getMainType</methodname>
378                <void />
379              </methodsynopsis>
380            </term>
381            <listitem>
382              <para>
383                Return information about the main type of plug-in. The
384                <classname docapi="net.sf.basedb.core.plugin">Plugin.MainType</classname>
385                is an enumeration with five possible values:
386                <itemizedlist>
387                  <listitem>
388                    <para>
389                      <constant>ANALYZE</constant>:
390                      An analysis plug-in
391                    </para>
392                  </listitem>
393                  <listitem>
394                    <para>
395                      <constant>EXPORT</constant>:
396                      A plug-in that exports data
397                    </para>
398                  </listitem>
399                  <listitem>
400                    <para>
401                      <constant>IMPORT</constant>:
402                      A plug-in that imports data
403                    </para>
404                  </listitem>
405                  <listitem>
406                    <para>
407                      <constant>INTENSITY</constant>:
408                      A plug-in that calculates the original spot intensities
409                      from raw data
410                    </para>
411                  </listitem>
412                  <listitem>
413                    <para>
414                      <constant>OTHER</constant>:
415                      Any other type of plug-in
416                    </para>
417                  </listitem>
418                </itemizedlist>
419                The returned value is stored in the database but is otherwise not used
420                by the core. Client applications (such as the web client) will probably
421                use this information to group the plug-ins, i.e., a button labeled
422                &gbExport;
423                will let you select among the export plug-ins.
424              </para>
425              <example id="net.sf.basedb.core.plugin.Plugin.getMainType">
426                <title>A typical implementation just return one of the values</title>
427<programlisting language="java">
428public Plugin.MainType getMainType()
429{
430   return Plugin.MainType.OTHER;
431}</programlisting>
432              </example>
433            </listitem>
434          </varlistentry>
435          <varlistentry>
436            <term>
437              <methodsynopsis language="java">
438                <modifier>public</modifier>
439                <type>boolean</type>
440                <methodname>supportsConfigurations</methodname>
441                <void />
442              </methodsynopsis>
443            </term>
444            <listitem>
445              <para>
446                If this method returns true, the plug-in can have different
447                configurations, (i.e.
448                <classname docapi="net.sf.basedb.core">PluginConfiguration</classname>).
449                Note that this method may return true even if the
450                <interfacename docapi="net.sf.basedb.core.plugin">InteractivePlugin</interfacename>
451                interface is not implemented. The
452                <classname docapi="net.sf.basedb.core.plugin">AbstractPlugin</classname>
453                returns true for this method, which is the old way before the
454                introduction of this method.
455              </para>
456            </listitem>
457          </varlistentry>
458          <varlistentry>
459            <term>
460              <methodsynopsis language="java">
461                <modifier>public</modifier>
462                <type>boolean</type>
463                <methodname>requiresConfiguration</methodname>
464                <void />
465              </methodsynopsis>
466            </term>
467            <listitem>
468              <para>
469                If this method returns true, the plug-in must have a configuration
470                to be able to run. For example, some of the core import plug-ins
471                must have information about the file format, to be able to import
472                any data.
473                The
474                <classname docapi="net.sf.basedb.core.plugin">AbstractPlugin</classname>
475                returns false for this method, which is the old way before the
476                introduction of this method.
477              </para>
478            </listitem>
479          </varlistentry>
480          <varlistentry>
481            <term>
482              <methodsynopsis language="java">
483                <modifier>public</modifier>
484                <type>Collection&lt;Permissions&gt;</type>
485                <methodname>getPermissions</methodname>
486                <void />
487              </methodsynopsis>
488            </term>
489            <listitem>
490              <para>
491                Return a collection of permissions that the plug-in needs
492                to be able to function as expected. This method may return null
493                or an empty collection. In this case the plug-in permission system
494                is not used and the plug-in always gets the same permissions as the
495                logged in user. If permissions are specified the plug-in should
496                list all permissions it requires. Permissions that are not listed
497                are denied.
498              </para>
499              <note>
500                <para>
501                The final assignment of permissions to a plug-in is always
502                at the hands of a server administrator. He/she may decide to
503                disable the plug-in permission system or revoke some of the
504                requested permissions. The permissions returned by this method is
505                only a recommendation that the server administrator may or may not
506                accept. See <xref linkend="plugins.permissions" />
507                for more information about plug-in permissions.
508                </para>
509              </note>
510            </listitem>
511          </varlistentry>
512          <varlistentry>
513            <term>
514              <methodsynopsis language="java">
515                <modifier>public</modifier>
516                <void />
517                <methodname>init</methodname>
518                <methodparam>
519                  <type>SessionControl</type>
520                  <parameter>sc</parameter>
521                </methodparam>
522                <methodparam>
523                  <type>ParameterValues</type>
524                  <parameter>configuration</parameter>
525                </methodparam>
526                <methodparam>
527                  <type>ParameterValues</type>
528                  <parameter>job</parameter>
529                </methodparam>
530                <exceptionname>BaseException</exceptionname>
531              </methodsynopsis>
532            </term>
533            <listitem>
534              <para>
535                Prepare the plug-in for execution or configuration. If the plug-in needs
536                to do some initialization this is the place to do it. A typical
537                implementation however only stores the passed parameters in instance
538                variables for later use. Since it is not possible to know what the user is going to
539                do at this stage, we recommend lazy initialisation of all other resources.
540              </para>
541              <para>
542                The parameters passed to this method has vital information that is
543                needed to execute the plug-in. The
544                <classname docapi="net.sf.basedb.core">SessionControl</classname>
545                is a central core object holding information about the logged in
546                user and is used to create
547                <classname docapi="net.sf.basedb.core">DbControl</classname>
548                objects which allows a plug-in to connect to the database to read, add or
549                update information. The two
550                <classname docapi="net.sf.basedb.core.plugin">ParameterValues</classname>
551                objects contain information about the configuration and job
552                parameters to the plug-in.
553                The <varname>configuration</varname> object holds all parameters stored
554                together with a <classname docapi="net.sf.basedb.core">PluginConfiguration</classname>
555                object in the database. If the plug-in is started without
556                a configuration this object is null.
557                The <varname>job</varname> object holds all parameters that are
558                stored together with a <classname docapi="net.sf.basedb.core">Job</classname> object in the
559                database. This object is null if the plug-in is started without a job.
560              </para>
561              <para>
562                The difference between a configuration parameter and a job parameter is
563                that a configuration is usually something an administrator sets up,
564                while a job is an actual execution of a plug-in. For example, a
565                configuration for an import plug-in holds the regular expressions needed
566                to parse a text file and find the headers, sections and data lines,
567                while the job holds the file to parse.
568              </para>
569              <para>
570                The
571                <classname docapi="net.sf.basedb.core.plugin">AbstractPlugin</classname>
572                contains an implementation of this method that saves the passed objects
573                in protected instance variables. If you override this method
574                we recommend that you also call <code>super.init()</code>.
575              </para>
576              <example id="net.sf.basedb.core.plugin.Plugin.init">
577                <title>The <classname docapi="net.sf.basedb.core.plugin">AbstractPlugin</classname> implementation of Plugin.init()</title>
578<programlisting language="java">
579protected SessionControl sc = null;
580protected ParameterValues configuration = null;
581protected ParameterValues job = null;
582/**
583   Store copies of the session control, plug-in and job configuration. These
584   are available to subclasses in the {@link #sc}, {@link #configuration}
585   and {@link #job} variables. If a subclass overrides this method it is
586   recommended that it also calls super.init(sc, configuration, job).
587*/
588public void init(SessionControl sc,
589   ParameterValues configuration, ParameterValues job)
590   throws BaseException
591{
592   this.sc = sc;
593   this.configuration = configuration;
594   this.job = job;
595}</programlisting>
596              </example>
597            </listitem>
598          </varlistentry>
599          <varlistentry>
600            <term>
601              <methodsynopsis language="java">
602                <modifier>public</modifier>
603                <void />
604                <methodname>run</methodname>
605                <methodparam>
606                  <type>Request</type>
607                  <parameter>request</parameter>
608                </methodparam>
609                <methodparam>
610                  <type>Response</type>
611                  <parameter>response</parameter>
612                </methodparam>
613                <methodparam>
614                  <type>ProgressReporter</type>
615                  <parameter>progress</parameter>
616                </methodparam>
617              </methodsynopsis>
618            </term>
619            <listitem>
620              <para>
621                Run the plug-in.
622              </para>
623              <para>
624                The <varname>request</varname>
625                parameter is of historical interest only. It has no useful information
626                and can be ignored.
627              </para>
628              <para>
629                The <varname>progress</varname> parameter
630                can be used by a plug-in to report its progress back to the core. The
631                core will usually send the progress information to the database, which
632                allows users to see exactly how the plug-in is progressing from the web
633                interface. This parameter can be null, but if it is not we recommend all
634                plug-ins to use it. However, it should be used sparingly, since each call
635                to set the progress may result in a database update. If the execution
636                involves several thousands of items it is a bad idea to update the
637                progress after processing each one of them. A good starting point is to
638                divide the work into 100 pieces each representing 1% of the work, i.e.,
639                if the plug-in should export 100 000 items it should report progress
640                after every 1000 items.
641              </para>
642              <para>
643                The <varname>response</varname>
644                parameter is used to tell the core if the plug-in was successful or
645                failed. Not setting a response is considered a failure by the core. From
646                the run method it is only allowed to use on of the
647                <methodname>Response.setDone()</methodname>,
648                <methodname>Response.setError()</methodname> or
649                <methodname>Response.setContinue()</methodname>
650                methods.
651              </para>
652             
653              <important>
654                It is also considered bad practice to let exceptions escape
655                out from this method. Always use <code>try...catch</code>
656                to catch exceptions and use <code>Response.setError()</code>
657                to report the error back to the core.
658              </important>
659             
660              <example id="net.sf.basedb.core.plugin.Plugin.run">
661                <title>
662                  Here is a skeleton that we recommend each plug-in to use in its
663                  implementation of the
664                  <methodname>run()</methodname>
665                  method
666                </title>
667<programlisting language="java">
668public void run(Request request, Response response, ProgressReporter progress)
669{
670   // Open a connection to the database
671   // sc is set by init() method
672   DbControl dc = sc.newDbControl();
673   try
674   {
675      // Insert code for plug-in here
676
677      // Commit the work
678      dc.commit();
679      response.setDone("Plug-in ended successfully");
680   }
681   catch (Throwable t)
682   {
683      // All exceptions must be catched and sent back
684      // using the response object
685      response.setError(t.getMessage(), Arrays.asList(t));
686   }
687   finally
688   {
689      // IMPORTANT!!! Make sure opened connections are closed
690      if (dc != null) dc.close();
691   }
692}</programlisting>
693              </example>
694            </listitem>
695          </varlistentry>
696          <varlistentry>
697            <term>
698              <methodsynopsis language="java">
699                <modifier>public</modifier>
700                <void />
701                <methodname>done</methodname>
702                <void />
703              </methodsynopsis>
704            </term>
705            <listitem>
706              <para>
707                Clean up all resources after executing the plug-in. This method must not
708                throw any exceptions.
709              </para>
710              <example id="net.sf.basedb.core.plugin.Plugin.done">
711                <title>
712                  The
713                  <classname docapi="net.sf.basedb.core.plugin">AbstractPlugin</classname>
714                  contains an implementation of the <methodname>done()</methodname>
715                  method simply sets the
716                  parameters passed to the
717                  <methodname>init()</methodname>
718                  method to null
719                </title>
720<programlisting language="java">
721/**
722   Clears the variables set by the init method. If a subclass
723   overrides this method it is recommended that it also calls super.done().
724*/
725public void done()
726{
727   configuration = null;
728   job = null;
729   sc = null;
730}</programlisting>
731              </example>
732            </listitem>
733          </varlistentry>
734        </variablelist>
735      </sect3>
736 
737      <sect3 id="plugin_developer.api.interfaces.interactive">
738        <title>The net.sf.basedb.core.plugin.InteractivePlugin interface</title>
739        <para>
740          If you want the plug-in to be able to interact with the user you must also implement
741          this interface. This is probably the case for most plug-ins. Among the core plug-ins
742          shipped with BASE the
743          <classname docapi="net.sf.basedb.plugins">SpotImageCreator</classname>
744          is one plug-in that does not interact with the user. Instead, the web client has
745          special JSP pages that handles all the interaction, creates a job for it and sets
746          the parameters. This, kind of hardcoded, approach can also be used for other
747          plug-ins, but then it usually requires modification of the client application
748          as well.
749        </para>
750        <para>
751          The
752          <interfacename docapi="net.sf.basedb.core.plugin">InteractivePlugin</interfacename>
753          has three main tasks:
754         
755          <orderedlist>
756          <listitem>
757            <para>
758            Tell a client application where the plug-in should be plugged
759            in.
760            </para>
761          </listitem>
762          <listitem>
763            <para>
764            Ask the users for configuration and job parameters.
765            </para>
766          </listitem>
767         
768          <listitem>
769            <para>
770            Validate parameter values entered by the user and store those in the
771            database.
772            </para>
773          </listitem>
774          </orderedlist>
775          This requires that the following methods are
776          implemented.
777        </para>
778       
779        <variablelist>
780          <varlistentry>
781            <term>
782              <methodsynopsis language="java">
783                <modifier>public</modifier>
784                <type>Set&lt;GuiContext&gt;</type>
785                <methodname>getGuiContexts</methodname>
786                <void />
787              </methodsynopsis>
788            </term>
789            <listitem>
790              <para>
791                Return information about where the plug-in should be plugged in. Each
792                place is identified by a
793                <classname docapi="net.sf.basedb.core.plugin">GuiContext</classname>
794                object, which is an
795                <classname docapi="net.sf.basedb.core">Item</classname>
796                and a
797                <classname docapi="net.sf.basedb.core">Type</classname>.
798                The item is one of the objects defined by the
799                <classname docapi="net.sf.basedb.core">Item</classname>
800                enumeration and the type is either
801                <constant>Type.LIST</constant>
802                or
803                <constant>Type.ITEM</constant>, which corresponde to the
804                list view and the single-item view in the web client.
805              </para>
806              <para>
807                For example, the
808                <varname>GuiContext</varname> =
809                (<constant>Item.REPORTER</constant>,
810                <constant>Type.LIST</constant>)
811                tells a client application that this plug-in can be plugged in whenever a
812                list of reporters is displayed. The
813                <varname>GuiContext</varname> =
814                (<constant>Item.REPORTER</constant>,
815                <constant>Type.ITEM</constant>)
816                tells a client application that this plug-in can be plugged in whenever
817                a single reporter is displayed. The first case may be appropriate for a
818                plug-in that imports or exports reporters. The second case may be used by
819                a plug-in that updates the reporter information from an external source
820                (well, it may make sense to use this in the list case as well).
821              </para>
822              <para>
823                The returned information is copied by the core at installation time to
824                make it easy to ask for all plug-ins for a certain
825                <classname docapi="net.sf.basedb.core.plugin">GuiContext</classname>.
826              </para>
827              <para>
828                A typical implementation creates a static unmodifiable
829                <classname>Set</classname>
830                which is returned by this method. It is important that the returned set
831                cannot be modified. It may be a security issue if a misbehaving
832                client application does that.
833              </para>
834              <example id="net.sf.basedb.core.plugin.InteractivePlugin.getGuiContexts">
835                <title>
836                  A typical implementation of
837                  <methodname>getGuiContexts</methodname>
838                </title>
839<programlisting language="java">
840// From the net.sf.basedb.plugins.RawDataFlatFileImporter plug-in
841private static final Set&lt;GuiContext&gt; guiContexts =
842   Collections.singleton(new GuiContext(Item.RAWBIOASSAY, GuiContext.Type.ITEM));
843
844public Set&lt;GuiContext&gt; getGuiContexts()
845{
846   return guiContexts;
847}</programlisting>
848              </example>
849            </listitem>
850          </varlistentry>
851          <varlistentry>
852            <term>
853              <methodsynopsis language="java">
854                <modifier>public</modifier>
855                <type>String</type>
856                <methodname>isInContext</methodname>
857                <methodparam>
858                  <type>GuiContext</type>
859                  <parameter>context</parameter>
860                </methodparam>
861                <methodparam>
862                  <type>Object</type>
863                  <parameter>item</parameter>
864                </methodparam>
865              </methodsynopsis>
866            </term>
867            <listitem>
868              <para>
869                This method is called to check if a particular item is usable for the
870                plug-in. This method is invoked to check if a plug-in can be used
871                in a given context. If invoked from a list context the <parameter>item</parameter>
872                parameter is <constant>null</constant>
873                The plug-in should return <constant>null</constant> if it
874                finds that it can be used. If the plug-in can't be used it
875                must decide if the reason should be a warning or an error condition.
876              </para>
877             
878              <para>
879                A warning is issued by returning a string with the warning
880                message. It should be used when the plug-in can't be used because
881                it is unrelated to the current task. For example, a plug-in for
882                importing Genepix data should return a warning when somebody wants
883                to import data to an Agilent raw bioassay.
884              </para>
885             
886              <para>
887                An error message is issued by throwing an exception. This
888                should be used when the plug-in is related to the current task
889                but still can't do what it is supposed to do. For example,
890                trying to import raw data if the logged in user doesn't have
891                write permission to the raw bioassay.
892              </para>
893             
894              <para>
895                As a rule of thumb, if there is a chance that another plug-in
896                might be able to perform the same task a warning should be used.
897                If it is guaranteed that no other plug-in can do it an error
898                message should be used.
899              </para>
900             
901              <para>
902                Here is a real example from the
903                <classname docapi="net.sf.basedb.plugins">RawDataFlatFileImporter</classname>
904                plug-in which imports raw data to a
905                <classname docapi="net.sf.basedb.core">RawBioAssay</classname>.
906               
907                Thus,
908                <varname>GuiContext</varname> =
909                (<constant>Item.RAWBIOASSAY</constant>,
910                <constant>Type.ITEM</constant>),
911               
912                but the plug-in can only import data if the logged in user has write permission,
913                there is no data already, and
914                if the raw bioassay has the same raw data type as the plug-in has been
915                configured for.
916              </para>
917              <example id="net.sf.basedb.core.plugin.InteractivePlugin.isInContext">
918                <title>
919                  A realistic implementation of the
920                  <methodname>isInContext()</methodname> method
921                </title>
922<programlisting language="java">
923/**
924   Returns null if the item is a {@link RawBioAssay} of the correct
925   {@link RawDataType} and doesn't already have spots.
926   @throws PermissionDeniedException If the raw bioasssay already has raw data
927   or if the logged in user doesn't have write permission
928*/
929public String isInContext(GuiContext context, Object item)
930{
931   String message = null;
932   if (item == null)
933   {
934      message = "The object is null";
935   }
936   else if (!(item instanceof RawBioAssay))
937   {
938      message = "The object is not a RawBioAssay: " + item;
939   }
940   else
941   {
942      RawBioAssay rba = (RawBioAssay)item;
943      String rawDataType = (String)configuration.getValue("rawDataType");
944      RawDataType rdt = rba.getRawDataType();
945      if (!rdt.getId().equals(rawDataType))
946      {
947         // Warning
948         message = "Unsupported raw data type: " + rba.getRawDataType().getName();
949      }
950      else if (!rdt.isStoredInDb())
951      {
952         // Warning
953         message = "Raw data for raw data type '" + rdt + "' is not stored in the database";
954      }
955      else if (rba.hasData())
956      {
957         // Error
958         throw new PermissionDeniedException("The raw bioassay already has data.");
959      }
960      else
961      {
962         // Error
963         rba.checkPermission(Permission.WRITE);
964      }
965   }
966   return message;   
967}</programlisting>
968              </example>
969            </listitem>
970          </varlistentry>
971          <varlistentry>
972            <term>
973              <methodsynopsis language="java">
974                <modifier>public</modifier>
975                <type>RequestInformation</type>
976                <methodname>getRequestInformation</methodname>
977                <methodparam>
978                  <type>GuiContext</type>
979                  <parameter>context</parameter>
980                </methodparam>
981                <methodparam>
982                  <type>String</type>
983                  <parameter>command</parameter>
984                </methodparam>
985                <exceptionname>BaseException</exceptionname>
986              </methodsynopsis>
987            </term>
988            <listitem>
989              <para>
990                Ask the plug-in for parameters that need to be entered by the user. The
991                <classname docapi="net.sf.basedb.core.plugin">GuiContext</classname>
992                parameter is one of the contexts returned by the
993                <methodname>getGuiContexts</methodname>
994                method. The command is a string telling the plug-in what command was
995                executed. There are two predefined commands but as you will see the
996                plug-in may define its own commands. The two predefined commands are
997                defined in the
998                <classname docapi="net.sf.basedb.core.plugin">net.sf.basedb.core.plugin.Request</classname>
999                class.
1000                <variablelist>
1001                  <varlistentry>
1002                    <term>
1003                      <constant>Request.COMMAND_CONFIGURE_PLUGIN</constant>
1004                    </term>
1005                    <listitem>
1006                      <para>
1007                        Used when an administrator is initiating a configuration
1008                        of the plug-in.
1009                      </para>
1010                    </listitem>
1011                  </varlistentry>
1012                  <varlistentry>
1013                    <term>
1014                      <constant>Request.COMMAND_CONFIGURE_JOB</constant>
1015                    </term>
1016                    <listitem>
1017                      <para>
1018                        Used when a user has selected the plug-in for running a
1019                        job.
1020                      </para>
1021                    </listitem>
1022                  </varlistentry>
1023                </variablelist>
1024                Given this information the plug-in must return a
1025                <classname docapi="net.sf.basedb.core">RequestInformation</classname>
1026                object. This is simply a title, a description, and a list of parameters.
1027                Usually the title will end up as the input form title and the
1028                description as a help text for the entire form. Do not put information
1029                about the individual parameters in this description, since each
1030                parameter has a description of its own.
1031              </para>
1032              <example id="net.sf.basedb.core.plugin.InteractivePlugin.getRequestInformation">
1033                <title>
1034                  When running an import plug-in it needs to ask for the file to import
1035                  from and if existing items should be updated or not
1036                </title>
1037<programlisting language="java">
1038// The complete request information
1039private RequestInformation configure Job;
1040
1041// The parameter that asks for a file to import from
1042private PluginParameter&lt;File&gt; file Parameter;
1043
1044// The parameter that asks if existing items should be updated or not
1045private PluginParameter&lt;Boolean&gt; updateExistingParameter;
1046
1047public RequestInformation getRequestInformation(GuiContext context, String command)
1048   throws BaseException
1049{
1050   RequestInformation requestInformation = null;
1051   if (command.equals(Request.COMMAND_CONFIGURE_PLUGIN))
1052   {
1053      requestInformation = getConfigurePlugin();
1054   }
1055   else if (command.equals(Request.COMMAND_CONFIGURE_JOB))
1056   {
1057      requestInformation = getConfigureJob();
1058   }
1059   return requestInformation;
1060}
1061
1062/**
1063   Get (and build) the request information for starting a job.
1064*/
1065private RequestInformation getConfigureJob()
1066{
1067   if (configureJob == null)
1068   {
1069      // A file is required
1070      fileParameter = new PluginParameter&lt;File&gt;(
1071         "file",
1072         "File",
1073         "The file to import the data from",
1074         new FileParameterType(null, true, 1)
1075      );
1076     
1077      // The default value is 'false'
1078      updateExistingParameter = new PluginParameter&lt;Boolean&gt;(
1079         "updateExisting",
1080         "Update existing items",
1081         "If this option is selected, already existing items will be updated " +
1082         " with the information in the file. If this option is not selected " +
1083         " existing items are left untouched.",
1084         new BooleanParameterType(false, true)
1085      );
1086
1087      List&lt;PluginParameter&lt;?&gt;&gt; parameters =
1088         new ArrayList&lt;PluginParameter&lt;?&gt;&gt;(2);
1089      parameters.add(fileParameter);
1090      parameters.add(updateExistingParameter);
1091     
1092      configureJob = new RequestInformation
1093      (
1094         Request.COMMAND_CONFIGURE_JOB,
1095         "Select a file to import items from",
1096         "Description",
1097         parameters
1098      );
1099   }
1100   return configureJob;
1101}</programlisting>
1102              </example>
1103              <para>
1104                As you can see it takes a lot of code to put together a
1105                <classname docapi="net.sf.basedb.core">RequestInformation</classname>
1106                object. For each parameter you need one
1107                <classname docapi="net.sf.basedb.core">PluginParameter</classname>
1108                object and one
1109                <classname docapi="net.sf.basedb.core">ParameterType</classname>
1110                object. To make life a little easier, a
1111                <classname docapi="net.sf.basedb.core">ParameterType</classname>
1112                can be reused for more than one
1113                <classname docapi="net.sf.basedb.core">PluginParameter</classname>.
1114              </para>
1115             
1116<programlisting language="java">
1117StringParameterType stringPT = new StringParameterType(255, null, true);
1118PluginParameter one = new PluginParameter("one", "One", "First string", stringPT);
1119PluginParameter two = new PluginParameter("two", "Two", "Second string", stringPT);
1120// ... and so on
1121</programlisting>
1122              <para>
1123                The
1124                <classname docapi="net.sf.basedb.core">ParameterType</classname>
1125                is an abstract base class for several subclasses each implementing a
1126                specific type of parameter. The list of subclasses may grow in the
1127                future, but here are the most important ones currently implemented.
1128              </para>
1129              <note>
1130                <para>
1131                  Most parameter types include support for supplying a predefined list
1132                  of options to select from. In that case the list will be displayed
1133                  as a drop-down list for the user, otherwise a free input field is
1134                  used.
1135                </para>
1136              </note>
1137              <variablelist>
1138                <varlistentry>
1139                  <term>
1140                    <classname docapi="net.sf.basedb.core">StringParameterType</classname>
1141                  </term>
1142                  <listitem>
1143                    <para>
1144                      Asks for a string value. Includes an option for
1145                      specifying the maximum length of the string.
1146                    </para>
1147                  </listitem>
1148                </varlistentry>
1149                <varlistentry>
1150                  <term>
1151                    <classname docapi="net.sf.basedb.core">FloatParameterType</classname>,
1152                    <classname docapi="net.sf.basedb.core">DoubleParameterType</classname>,
1153                    <classname docapi="net.sf.basedb.core">IntegerParameterType</classname>,
1154                    <classname docapi="net.sf.basedb.core">LongParameterType</classname>
1155                  </term>
1156                  <listitem>
1157                    <para>
1158                      Asks for numerical values. Includes options for
1159                      specifying a range (min/max) of allowed values.
1160                    </para>
1161                  </listitem>
1162                </varlistentry>
1163                <varlistentry>
1164                  <term>
1165                    <classname docapi="net.sf.basedb.core">BooleanParameterType</classname>
1166                  </term>
1167                  <listitem>
1168                    <para>Asks for a boolean value.
1169                    </para>
1170                  </listitem>
1171                </varlistentry>
1172                <varlistentry>
1173                  <term>
1174                    <classname docapi="net.sf.basedb.core">DateParameterType</classname>
1175                  </term>
1176                  <listitem>
1177                    <para>Asks for a date.
1178                    </para>
1179                  </listitem>
1180                </varlistentry>
1181                <varlistentry>
1182                  <term>
1183                    <classname docapi="net.sf.basedb.core">FileParameterType</classname>
1184                  </term>
1185                  <listitem>
1186                    <para>Asks for a file item.
1187                    </para>
1188                  </listitem>
1189                </varlistentry>
1190                <varlistentry>
1191                  <term>
1192                    <classname docapi="net.sf.basedb.core">ItemParameterType</classname>
1193                  </term>
1194                  <listitem>
1195                    <para>
1196                      Asks for any other item. This parameter type requires
1197                      that a list of options is supplied, except when the item
1198                      type asked for matches the current <classname docapi="net.sf.basedb.core.plugin">GuiContext</classname>, in which
1199                      case the currently selected item is used as the
1200                      parameter value.
1201                    </para>
1202                  </listitem>
1203                </varlistentry>
1204                <varlistentry>
1205                  <term>
1206                    <classname docapi="net.sf.basedb.core">PathParameterType</classname>
1207                  </term>
1208                  <listitem>
1209                    <para>
1210                      Ask for a path to a file or directory. The path may be
1211                      non-existing and should be used when a plug-in needs an
1212                      output destination, i.e., the file to export to, or a
1213                      directory where the output files should be placed.
1214                    </para>
1215                  </listitem>
1216                </varlistentry>
1217              </variablelist>
1218              <para>
1219                You can also create a
1220                <classname docapi="net.sf.basedb.core">PluginParameter</classname>
1221                with a null name and
1222                <classname docapi="net.sf.basedb.core">ParameterType</classname>.
1223                In this case, the web client will not ask for input from the user, instead
1224                it is used as a section header, allowing you to group parameters into
1225                different sections which increase the readability of the input
1226                parameters page.
1227              </para>
1228<programlisting language="java">
1229PluginParameter firstSection = new PluginParameter(null, "First section", null, null);
1230PluginParameter secondSection = new PluginParameter(null, "Second section", null, null);
1231// ...
1232
1233parameters.add(firstSection);
1234parameters.add(firstParameterInFirstSection);
1235parameters.add(secondParameteInFirstSection);
1236
1237parameters.add(secondSection);
1238parameters.add(firstParameterInSecondSection);
1239parameters.add(secondParameteInSecondSection);
1240</programlisting>
1241            </listitem>
1242          </varlistentry>
1243          <varlistentry>
1244            <term>
1245              <methodsynopsis language="java">
1246                <modifier>public</modifier>
1247                <void />
1248                <methodname>configure</methodname>
1249                <methodparam>
1250                  <type>GuiContext</type>
1251                  <parameter>context</parameter>
1252                </methodparam>
1253                <methodparam>
1254                  <type>Request</type>
1255                  <parameter>request</parameter>
1256                </methodparam>
1257                <methodparam>
1258                  <type>Response</type>
1259                  <parameter>response</parameter>
1260                </methodparam>
1261              </methodsynopsis>
1262            </term>
1263            <listitem>
1264              <para>
1265                Sends parameter values entered by the user for processing by the plug-in.
1266                The plug-in must validate that the parameter values are
1267                correct and then store them in database.
1268              </para>
1269             
1270              <important>
1271                <para>
1272                No validation is done by the core, except converting the input to the
1273                correct object type, i.e. if the plug-in asked for a
1274                <classname>Float</classname>
1275                the input string is parsed and converted to a
1276                <classname>Float</classname>. If you have extended the
1277                <classname docapi="net.sf.basedb.core.plugin">AbstractPlugin</classname>
1278                class it is very easy to validate the parameters with the
1279                <methodname>AbstractPlugin.validateRequestParameters()</methodname>
1280                method. This method takes the same list of
1281                <classname docapi="net.sf.basedb.core">PluginParameter</classname>:s
1282                as used in the
1283                <classname docapi="net.sf.basedb.core">RequestInformation</classname>
1284                object and uses that information for validation. It returns null or a
1285                list of
1286                <exceptionname>Throwable</exceptionname>:s that can be
1287                given directly to the <code>response.setError()</code>
1288                methods.
1289                </para>
1290              </important>
1291              <para>
1292                When the parameters have been validated, they need to be stored
1293                in the database. Once again, it is very easy, if you use one of the
1294                <methodname>AbstractPlugin.storeValue()</methodname>
1295                or
1296                <methodname>AbstractPlugin.storeValues()</methodname>
1297                methods.
1298              </para>
1299              <para>
1300                The configure method works much like the <methodname>Plugin.run()</methodname> 
1301                method. It must return the result in the <classname docapi="net.sf.basedb.core.plugin">Response</classname> object,
1302                and should not throw any exceptions.
1303              </para>
1304             
1305              <example id="net.sf.basedb.core.plugin.InteractivePlugin.configure">
1306                <title>
1307                  Configuration implementation building on the examples above
1308                </title>
1309<programlisting language="java">
1310public void configure(GuiContext context, Request request, Response response)
1311{
1312   String command = request.getCommand();
1313   try
1314   {
1315      if (command.equals(Request.COMMAND_CONFIGURE_PLUGIN))
1316      {
1317         // TODO
1318      }
1319      else if (command.equals(Request.COMMAND_CONFIGURE_JOB))
1320      {
1321         // Validate user input
1322         List&lt;Throwable&gt; errors =
1323            validateRequestParameters(getConfigureJob().getParameters(), request);
1324         if (errors != null)
1325         {
1326            response.setError(errors.size() +
1327               " invalid parameter(s) were found in the request", errors);
1328            return;
1329         }
1330         
1331         // Store user input
1332         storeValue(job, request, fileParameter);
1333         storeValue(job, request, updateExistingParameter);
1334         
1335         // We are happy and done
1336         File file = (File)job.getValue("file");
1337         response.setSuggestedJobName("Import data from file " + file.getName());
1338         response.setDone("Job configuration complete", Job.ExecutionTime.SHORT);
1339      }
1340   }
1341   catch (Throwable ex)
1342   {
1343      response.setError(ex.getMessage(), Arrays.asList(ex));
1344   }
1345}</programlisting>
1346              </example>
1347             
1348              <para>
1349                Note that the call to
1350                <methodname>response.setDone()</methodname>
1351                has a second parameter
1352                <constant>Job.ExecutionTime.SHORT</constant>. It is an indication
1353                about how long time it will take to execute the
1354                plug-in. This is of interest for job queue managers which probably
1355                does not want to start too many long-running jobs at the same time
1356                blocking the entire system. Please try to use this parameter wisely and
1357                not use <constant>Job.ExecutionTime.SHORT</constant>
1358                out of old habit all the time.
1359              </para>
1360              <para>
1361                The <classname docapi="net.sf.basedb.core.plugin">Response</classname> class also has a
1362                <methodname>setContinue()</methodname>
1363                method which tells the core that the plug-in needs more parameters,
1364                i.e. the core will then call
1365                <methodname>getRequestInformation()</methodname>
1366                again with the new command, let the user enter values, and then call
1367                <methodname>configure()</methodname>
1368                with the new values. This process is repeated until the plug-in
1369                reports that it is done or an error occurs.
1370              </para>
1371              <tip>
1372                <para>
1373                You do not have to store all values the plug-in asked for in the
1374                first place. You may even choose to store different values than
1375                those that were entered. For example, you might ask for the mass
1376                and height of a person and then only store the body mass index,
1377                which is calculated from those values.
1378                </para>
1379              </tip>
1380              <para>
1381                An important note is that during this iteration it is the same instance
1382                of the plug-in that is used. However, no parameter values are stored in
1383                the database until the plugin sends a
1384                <methodname>response.setDone()</methodname>.
1385                After that, the plug-in instance is usually discarded, and a job is placed
1386                in the job queue. The execution of the plug-in happens in a new instance
1387                and maybe on a different server. This means that a plug-in can't store
1388                state from the configuration phase internally and expect it to be there
1389                in the execution phase. Everything the plug-in needs to do it's job must
1390                be stored as parameters in the database.
1391              </para>
1392              <para>
1393                The only exception to the above rule is if the plug-in answers with
1394                <methodname>Response.setExecuteImmediately()</methodname>
1395                or <methodname>Response.setDownloadImmediately()</methodname>.
1396                Doing so bypasses the entire job queue system and requests that the
1397                job is started immediately. This is a permission that has to
1398                be granted to each plug-in by the server administrator. If the
1399                plug-in has this permission, the same object instance that
1400                was used in the configuration phase is also used in the execution
1401                phase. This is the only case where a plug-in can retain internal
1402                state between the two phases.
1403              </para>
1404            </listitem>
1405          </varlistentry>
1406        </variablelist>
1407      </sect3>
1408   
1409    </sect2>
1410 
1411    <sect2 id="plugin_developer.api.callsequence">
1412      <title>How the BASE core interacts with the plug-in when...</title>
1413     
1414      <para>
1415        This section describes how the BASE core interacts with the
1416        plug-in in a number of use cases. We will outline the
1417        order the methods are invoked on the plug-in.
1418      </para>
1419     
1420      <sect3 id="plugin_developer.api.callsequence.install">
1421        <title>Installing a plug-in</title>
1422       
1423        <para>
1424          When a plug-in is installed the core is eager to find out
1425          information about the plug-in. To do this it calls the
1426          following methods in this order:
1427        </para>
1428       
1429        <orderedlist>
1430        <listitem>
1431          <para>
1432          A new instance of the plug-in class is created. The plug-in must
1433          have a public no-argument constructor.
1434          </para>
1435        </listitem>
1436       
1437        <listitem>
1438          <para>
1439          Calls are made to <methodname>Plugin.getMainType()</methodname>,
1440          <methodname>Plugin.supportsConfigurations()</methodname> and
1441          <methodname>Plugin.requiresConfiguration()</methodname> to find out information
1442          about the plug-in. This is the only time these methods are called.
1443          The information that is returned by them are copied and stored in
1444          the database for easy access.
1445          </para>
1446         
1447          <note>
1448            <para>
1449            The <methodname>Plugin.init()</methodname> method is
1450            never called during plug-in installation.
1451            </para>
1452          </note>
1453        </listitem>
1454       
1455        <listitem>
1456          <para>
1457          If the plug-in implements the <interfacename docapi="net.sf.basedb.core.plugin">InteractivePlugin</interfacename>
1458          interface the <methodname>InteractivePlugin.getGuiContexts()</methodname>
1459          method is called. This is the only time this method is called and the information it
1460          returns are copied and stored in the database.
1461          </para>
1462        </listitem>
1463       
1464        <listitem>
1465          <para>
1466          If the server admin decided to use the plug-in permission system, the
1467          <methodname>Plugin.getPermissions()</methodname> method is called.
1468          The returned information is copied and stored in the database.
1469          </para>
1470        </listitem>
1471       
1472        </orderedlist>
1473      </sect3>
1474   
1475      <sect3 id="plugin_developer.api.callsequence.configure">
1476        <title>Configuring a plug-in</title>
1477       
1478        <para>
1479          The plug-in must implement the <interfacename docapi="net.sf.basedb.core.plugin">InteractivePlugin</interfacename>
1480          interface and the <methodname>Plugin.supportsConfigurations()</methodname> method
1481          must return <constant>TRUE</constant>. The configuration is done with
1482          a wizard-like interface (see <xref linkend="plugins.configuration.wizard" />).
1483          The same plug-in instance is used throughout the entire configuration sequence.
1484        </para>
1485       
1486        <orderedlist>
1487        <listitem>
1488          <para>
1489          A new instance of the plug-in class is created. The plug-in must
1490          have a public no-argument constructor.
1491          </para>
1492        </listitem>
1493       
1494        <listitem>
1495          <para>
1496          The <methodname>Plugin.init()</methodname> method is called. The
1497          <varname>job</varname> parameter is null.
1498          </para>
1499        </listitem>
1500       
1501        <listitem id="plugin_developer.api.callsequence.configure.requestinformation">
1502          <para>
1503          The <methodname>InteractivePlugin.getRequestInformation()</methodname> 
1504          method is called. The <varname>context</varname> parameter is <constant>null</constant>
1505          and the <varname>command</varname> is the value of the string
1506          constant <constant>Request.COMMAND_CONFIGURE_PLUGIN</constant> 
1507          (<constant>_config_plugin</constant>).
1508          </para>
1509        </listitem>
1510       
1511        <listitem id="plugin_developer.api.callsequence.configure.form">
1512          <para>
1513          The web client process the returned information and displays a form for user
1514          input. The plug-in will have to wait some time while the user enters
1515          data.
1516          </para>
1517        </listitem>
1518       
1519        <listitem>
1520          <para>
1521          The <methodname>InteractivePlugin.configure()</methodname> method
1522          is called. The <varname>context</varname> parameter is still
1523          <constant>null</constant> and the <varname>request</varname>
1524          parameter contains the parameter values entered by the user.
1525          </para>
1526        </listitem>
1527       
1528        <listitem>
1529          <para>
1530          The plug-in must validate the values and decide whether they should be
1531          stored in the database or not. We recommend that you use the
1532          methods in the <classname docapi="net.sf.basedb.core.plugin">AbstractPlugin</classname> class for this.
1533          </para>
1534        </listitem>
1535       
1536        <listitem>
1537          <para>
1538          The plug-in can choose between three different respones:
1539         
1540          <itemizedlist>
1541          <listitem>
1542            <para>
1543            <methodname>Response.setDone()</methodname>: The configuration
1544            is complete. The core will write any configuation changes to the
1545            database, call the <methodname>Plugin.done()</methodname> method and
1546            then discard the plug-in instance.
1547            </para>
1548          </listitem>
1549         
1550          <listitem>
1551            <para>
1552            <methodname>Response.setError()</methodname>: There was one or more
1553            errors. The web client will display the error messages for the user and
1554            allow the user to enter new values. The process continues with
1555            step <xref linkend="plugin_developer.api.callsequence.configure.form" />.
1556            </para>
1557          </listitem>
1558         
1559          <listitem>
1560            <para>
1561            <methodname>Response.setContinue()</methodname>: The parameters are correct
1562            but the plug-in wants more parameters. The process continues with
1563            step <xref linkend="plugin_developer.api.callsequence.configure.requestinformation" />
1564            but the <varname>command</varname> has the value that was passed to the
1565            <methodname>setContinue()</methodname> method.
1566            </para>
1567          </listitem>
1568          </itemizedlist>
1569         
1570          </para>
1571        </listitem>
1572        </orderedlist>
1573
1574      </sect3>   
1575   
1576      <sect3 id="plugin_developer.api.callsequence.context">
1577        <title>Checking if a plug-in can be used in a given context</title>
1578       
1579        <para>
1580          If the plug-in is an <interfacename docapi="net.sf.basedb.core.plugin">InteractivePlugin</interfacename>
1581          it has specified in which contexts it can be used by the
1582          information returned from <methodname>InteractivePlugin.getGuiContexts()</methodname>
1583          method. The web client uses this information to decide whether,
1584          for example, a <guibutton>Run plugin</guibutton>
1585          button should be displayed on a page or not. However, this is not
1586          always enough to know whether the plug-in can be used or not.
1587          For example, a raw data importer plug-in cannot be used to
1588          import raw data if the raw bioassay already has data.
1589          So, when the user clicks the button, the web client will
1590          load all plug-ins that possibly can be used in the given context
1591          and let each one of them check whether they can be used or not.
1592        </para>
1593       
1594     
1595        <orderedlist>
1596        <listitem>
1597          <para>
1598          A new instance of the plug-in class is created. The plug-in must
1599          have a public no-argument constructor.
1600          </para>
1601        </listitem>
1602       
1603        <listitem>
1604          <para>
1605          The <methodname>Plugin.init()</methodname> method is called.
1606          The <varname>job</varname> parameter is <constant>null</constant>.
1607          The <varname>configuration</varname> parameter is <constant>null</constant>
1608          if the plug-in does not have any configuration parameters.
1609          </para>
1610        </listitem>
1611       
1612        <listitem>
1613          <para>
1614          The <methodname>InteractivePlugin.isInContext()</methodname>
1615          is called. If the context is a list context, the <varname>item</varname>
1616          parameter is null, otherwise the current item is passed. The plug-in
1617          should return <constant>null</constant> if it can be used under the
1618          current circumstances, or a message explaining why not.
1619          </para>
1620        </listitem>
1621       
1622        <listitem>
1623          <para>
1624            After this, <methodname>Plugin.done()</methodname> is called and
1625            the plug-in instance is discarded. If there are
1626            several configurations for a plug-in, this procedure is repeated
1627            for each configuration.
1628          </para>
1629        </listitem>
1630        </orderedlist>
1631      </sect3>
1632   
1633      <sect3 id="plugin_developer.api.callsequence.job">
1634        <title>Creating a new job</title>
1635       
1636        <para>
1637          If the web client found that the plug-in could be
1638          used in a given context and the user selected the plug-in,
1639          the job configuration sequence is started. It is a wizard-like interface
1640          identical to the configuration wizard. In fact, the same JSP pages,
1641          and calling sequence is used. See <xref linkend="plugin_developer.api.callsequence.configure" />.
1642          We do not repeat everything here. There are a few differences:
1643        </para>
1644       
1645        <itemizedlist>
1646        <listitem>
1647          <para>
1648          The <varname>job</varname> parameter is not null, but it does not contain
1649          any parameter values to start with. The plug-in should use this
1650          object to store job-related parameter values. The
1651          <varname>configuration</varname> parameter is <constant>null</constant> 
1652          if the plug-in is started without configuration. In any case,
1653          the configuration values are write-protected and cannot be modified.
1654          </para>
1655        </listitem>
1656       
1657        <listitem>
1658          <para>
1659          The first call to <methodname>InteractivePlugin.getRequestInformation()</methodname>
1660          is done with <constant>Request.COMMAND_CONFIGURE_JOB</constant> (<constant>_configjob</constant>)
1661          as the command. The <varname>context</varname> parameter reflects the
1662          current context.
1663          </para>
1664        </listitem>
1665       
1666        <listitem>
1667          <para>
1668          When calling <methodname>Response.setDone()</methodname> the plug-in
1669          should use the variant that takes an estimated execution time.
1670          If the plug-in has support for immediate execution or download
1671          (export plug-ins only), it can also respond with
1672          <methodname>Response.setExecuteImmediately()</methodname> or
1673          <methodname>Response.setDownloadImmediately()</methodname>.
1674          </para>
1675         
1676          <para>
1677          If the plug-in requested and was granted immediate execution or
1678          download the same plug-in instance is used to execute the plug-in.
1679          This may be done with the same or a new thread. Otherwise, a new
1680          job is added to the job queue, the parameter value are saved
1681          and the plug-in instance is discarded after calling the
1682          <methodname>Plugin.done()</methodname> method.
1683          </para>
1684        </listitem>
1685        </itemizedlist>
1686      </sect3>
1687   
1688      <sect3 id="plugin_developer.api.callsequence.execute">
1689        <title>Executing a job</title>
1690       
1691        <para>
1692          Normally, the creation of a job and the execution of it are
1693          two different events. The execution may as well be done on a
1694          different server. See <xref linkend="installation.jobagents" />.
1695          This means that the execution takes place in a different instance
1696          of the plug-in class than what was used for creating the job.
1697          The exception is if a plug-in supports immediate execution or download.
1698          In this case the same instance is used, and it is, of course,
1699          always executed on the web server.
1700        </para>
1701       
1702        <orderedlist>
1703        <listitem>
1704          <para>
1705          A new instance of the plug-in class is created. The plug-in must
1706          have a public no-argument constructor.
1707          </para>
1708        </listitem>
1709       
1710        <listitem>
1711          <para>
1712          The <methodname>Plugin.init()</methodname> method is called.
1713          The <varname>job</varname> parameter contains the job
1714          configuration parameters. The <varname>configuration</varname> parameter
1715          is <constant>null</constant> if the plug-in does not have any
1716          configuration parameters.
1717          </para>
1718        </listitem>
1719       
1720        <listitem>
1721          <para>
1722          The <methodname>Plugin.run()</methodname> method is called.
1723          It is finally time for the plug-in to do the work it has been
1724          designed for. This method should not throw any exceptions.
1725          Use the <methodname>Response.setDone()</methodname>
1726          method to report success, the <methodname>Response.setError()</methodname>
1727          method to report errors or the <methodname>Response.setContinue()</methodname>
1728          method to respond to a shutdown signal and tell the core to resume the
1729          job once the system is up and running again. The <methodname>Response.setContinue()</methodname>
1730          can also be used when the system is not shutting down. The job will then be
1731          put back into the job queue and executed again later.
1732          </para>
1733        </listitem>
1734       
1735        <listitem>
1736          <para>
1737          In all cases the <methodname>Plugin.done()</methodname>
1738          method is called and the plug-in instance is discarded.
1739          </para>
1740        </listitem>
1741       
1742        </orderedlist>
1743      </sect3>
1744   
1745    </sect2>
1746 
1747    <sect2 id="plugin_developer.api.jspparameters">
1748      <title>Using custom JSP pages for parameter input</title>
1749
1750        <para>
1751          This is an advanced option for plug-ins that require a different interface for
1752          specifying plug-in parameters than the default list showing one parameter at a
1753          time. This feature is used by setting the
1754          <methodname>RequestInformation.getJspPage()</methodname>
1755          property when constructing the request information object. If this property has
1756          a non-null value, the web client will send the browser to the specified JSP page
1757          instead of to the generic parameter input page.
1758        </para>
1759        <para>
1760          When setting the JSP page you can either specify an absolute path
1761          or only the filename of the JSP file. If only the filename is specified,
1762          the JSP file is expected to be located in a special location, generated
1763          from the package name of your plug-in. If the plug-in is located in the package
1764          <classname>org.company</classname> the JSP file must be located in
1765          <filename class="directory">&lt;base-dir&gt;/www/plugins/org/company/</filename>.
1766        </para>
1767       
1768        <para>
1769          An absolute path starts with '/' and may or may not include the
1770          root directory of the BASE installation. If, for example, BASE is intalled to
1771          <userinput>http://your.base.server.com/base</userinput>, the following absolute
1772          paths are equivalent  <filename>/base/path/to/file.jsp</filename>,
1773          <filename>/path/to/file.jsp</filename>.
1774        </para>
1775        <para>
1776          In both cases, please note that the browser still thinks that it is showing
1777          the regular parameter input page at the usual location:
1778          <filename class="directory">&lt;base-dir&gt;/www/common/plugin/index.jsp</filename>.
1779          All links in your JSP page should be relative to that directory.
1780        </para>
1781        <para>
1782          Even if you use your own JSP page we recommend that you use the built-in
1783          facility for passing the parameters back to the plug-in. For this to work you
1784          must:
1785        </para>
1786        <itemizedlist spacing="compact">
1787          <listitem>
1788            <simpara>
1789            Generate the list of <classname docapi="net.sf.basedb.core">PluginParameter</classname> 
1790            objects as usual.
1791            </simpara>
1792          </listitem>
1793          <listitem>
1794            <simpara>
1795              Name all your input fields in the JSP like:
1796              <parameter>
1797                parameter:<replaceable>name-of-parameter</replaceable>
1798              </parameter>
1799            </simpara>
1800<programlisting language="java:nogutter">
1801// Plug-in generate PluginParameter
1802StringParameterType stringPT = new StringParameterType(255, null, true);
1803PluginParameter one = new PluginParameter("one", "One", "First string", stringPT);
1804PluginParameter two = new PluginParameter("two", "Two", "Second string", stringPT);
1805
1806// JSP should name fields as:
1807First string: &lt;input type="text" name="parameter:one"&gt;&lt;br&gt;
1808Second string: &lt;input type="text" name="parameter:two"&gt;
1809</programlisting>
1810          </listitem>
1811          <listitem>
1812          <simpara>
1813            Send the form to
1814            <filename>index.jsp</filename>
1815            with the <varname>ID</varname>,
1816            <varname>cmd</varname> and <varname>requestId</varname> 
1817            parameters as shown below.
1818          </simpara>
1819<programlisting language="xml">
1820<![CDATA[
1821<form action="index.jsp" method="post">
1822<input type="hidden" name="ID" value="<%=ID%>">
1823<input type="hidden" name="requestId" value="<%=request.getParameter("requestId")%>">
1824<input type="hidden" name="cmd" value="SetParameters">
1825...
1826</form>
1827]]>
1828</programlisting>
1829          <simpara>
1830            The <varname>ID</varname> is the session ID for the logged
1831            in user and is required. The <varname>requestId</varname>
1832            is the ID for this particular plug-in/job configuration
1833            sequence. It is optional, but we recommend that you use it
1834            since it protects your plug-in from getting mixed up with
1835            other plug-in configuration wizards. The <varname>cmd=SetParameters</varname>
1836            tells BASE to send the parameters to the plug-in for validation
1837            and saving.
1838          </simpara>
1839
1840          <simpara>
1841            Values are sent as strings to BASE that converts them to the
1842            proper value type before they are passed on to your plug-in.
1843            However, there is one case that can't be
1844            accurately represented with custom JSP pages, namely 'null' values.
1845            A null value is sent by not sending any value at all. This is not
1846            possible with a fixed form. It is of course possible to add some custom
1847            JavaScript that adds and removes form elements as needed, but it is
1848            also possible to let the empty string represent null. Just include a
1849            hidden parameter like this if you want an empty value for the 'one'
1850            parameter converted to null:
1851          </simpara>
1852<programlisting language="xml">
1853&lt;input type="hidden" name="parameter:one:emptyIsNull" value="1"&gt;
1854</programlisting>
1855          </listitem>
1856          </itemizedlist>
1857         
1858          <para>
1859            If you want a &gbCancel; button to abort the configuration
1860            this should be linked to a page reload with with the url:
1861            <uri>index.jsp?ID=&lt;%=ID%&gt;&amp;cmd=CancelWizard</uri>. This
1862            allows BASE to clean up resources that has been put in global
1863            session variables.
1864          </para>
1865         
1866            <para>
1867              In your JSP page you will probably need to access some information like the
1868              <classname docapi="net.sf.basedb.core">SessionControl</classname>, <classname docapi="net.sf.basedb.core">Job</classname>
1869              and possible even the <classname docapi="net.sf.basedb.core">RequestInformation</classname>
1870              object created by your plug-in.
1871            </para>
1872<programlisting language="java">
1873// Get session control and its ID (required to post to index.jsp)
1874final SessionControl sc = Base.getExistingSessionControl(pageContext, true);
1875final String ID = sc.getId();
1876
1877// Get information about the current request to the plug-in
1878PluginConfigurationRequest pcRequest =
1879   (PluginConfigurationRequest)sc.getSessionSetting("plugin.configure.request");
1880PluginDefinition plugin =
1881   (PluginDefinition)sc.getSessionSetting("plugin.configure.plugin");
1882PluginConfiguration pluginConfig =
1883   (PluginConfiguration)sc.getSessionSetting("plugin.configure.config");
1884PluginDefinition job =
1885   (PluginDefinition)sc.getSessionSetting("plugin.configure.job");
1886RequestInformation ri = pcRequest.getRequestInformation();
1887</programlisting>
1888
1889    </sect2>
1890  </sect1>
1891
1892  <sect1 id="plugin_developer.import">
1893    <?dbhtml filename="import.html" ?>
1894    <title>Import plug-ins</title>
1895
1896    <para>
1897      A plug-in becomes an import plugin simply by returning
1898      <constant>Plugin.MainType.IMPORT</constant>
1899      from the <methodname>Plugin.getMainType()</methodname> method.
1900    </para>
1901   
1902    <sect2 id="plugin_developer.import.autodetect">
1903      <title>Autodetect file formats</title>
1904      <para>
1905        BASE has built-in functionality for autodetecting file formats.
1906        Your plug-in can be part of that feature if it reads it data
1907        from a single file. It must also implement the
1908        <interfacename docapi="net.sf.basedb.core.plugin">AutoDetectingImporter</interfacename>
1909        interface.
1910      </para>
1911
1912      <sect3 id="plugin_developer.api.interfaces.autodetecting">
1913        <title>The net.sf.basedb.core.plugin.AutoDetectingImporter interface</title>
1914
1915        <variablelist>
1916        <varlistentry>
1917          <term>
1918            <methodsynopsis language="java">
1919              <modifier>public</modifier>
1920              <type>boolean</type>
1921              <methodname>isImportable</methodname>
1922              <methodparam>
1923                <type>InputStream</type>
1924                <parameter>in</parameter>
1925              </methodparam>
1926              <exceptionname>BaseException</exceptionname>
1927            </methodsynopsis>
1928          </term>
1929          <listitem>
1930            <para>
1931              Check the input stream if it seems to contain data that can be imported by
1932              the plugin. Usually it means scanning a few lines for some header
1933              matching a predefined string or regular expression.
1934            </para>
1935            <para>
1936              The <classname docapi="net.sf.basedb.plugins">AbstractFlatFileImporter</classname> 
1937              can be used for text-based files and implements this method
1938              by reading the headers from the input stream and checking if
1939              it stopped at an unknown type of line or not:
1940                <programlisting language="java">
1941public final boolean isImportable(InputStream in)
1942   throws BaseException
1943{
1944   FlatFileParser ffp = getInitializedFlatFileParser();
1945   ffp.setInputStream(in);
1946   try
1947   {
1948      ffp.nextSection();
1949      FlatFileParser.LineType result = ffp.parseHeaders();
1950      if (result == FlatFileParser.LineType.UNKNOWN)
1951      {
1952         return false;
1953      }
1954      else
1955      {
1956         return isImportable(ffp);
1957      }
1958   }
1959   catch (IOException ex)
1960   {
1961      throw new BaseException(ex);
1962   }
1963}
1964</programlisting>
1965           
1966              The <classname>AbstractFlatFileImporter</classname> also has functions
1967              for setting the character set and automatic unwrapping of compressed
1968              files. See the javadoc for more information.
1969            </para>
1970            <para>
1971              Note that the input stream doesn't have to be a text file (but you can't use the
1972              <classname>AbstractFlatFileImporter</classname> then).
1973              It can be any type of file, for example a binary or an XML file.
1974              In the case of an XML file you would need to validate the entire
1975              input stream in order to be a 100% sure that it is a valid
1976              xml file, but we recommend that you only check the first few XML tags,
1977              for example, the &lt;!DOCTYPE &gt; declaration and/or the root element
1978              tag.
1979            </para>
1980           
1981          <tip>
1982          <title>Try casting to ImportInputStream</title>
1983          <para>
1984            In many cases (but not all) the auto-detect functionality uses a
1985            <classname docapi="net.sf.basedb.core.plugin">ImportInputStream</classname>
1986            as the <varname>in</varname> parameter. This class contains some
1987            metadata about the file the input stream is originating from. The most
1988            useful feature is the possibility to get information about the
1989            character set used in the file. This makes it possible to open text
1990            files using the correct character set.
1991          </para>
1992          <programlisting language="java">
1993String charset = Config.getCharset(); // Default value
1994if (in instanceof ImportInputStream)
1995{
1996   ImportInputStream iim = (ImportInputStream)in;
1997   if (iim.getCharacterSet() != null) charset = iim.getCharacterSet();
1998}
1999Reader reader = new InputStreamReader(in, Charset.forName(charset)));
2000</programlisting>
2001        </tip>       
2002           
2003          </listitem>
2004        </varlistentry>
2005        <varlistentry>
2006          <term>
2007            <methodsynopsis language="java">
2008              <modifier>public</modifier>
2009              <void/>
2010              <methodname>doImport</methodname>
2011              <methodparam>
2012                <type>InputStream</type>
2013                <parameter>in</parameter>
2014              </methodparam>
2015              <methodparam>
2016                <type>ProgressReporter</type>
2017                <parameter>progress</parameter>
2018              </methodparam>
2019              <exceptionname>BaseException</exceptionname>
2020            </methodsynopsis>
2021          </term>
2022          <listitem>
2023            <para>
2024              Parse the input stream and import all data that is found.
2025              This method is of course only called if the
2026              <methodname>isImportable()</methodname> has returned true. Note
2027              however that the input stream is reopened at the start of the
2028              file. It may even be the case that the <methodname>isImportable()</methodname>
2029              method is called on one instance of the plugin and the
2030              <methodname>doImport()</methodname> method is called on another.
2031              Thus, the <methodname>doImport()</methodname> can't rely on any state set
2032              by the <methodname>isImportable()</methodname> method.
2033            </para>
2034          </listitem>
2035        </varlistentry>
2036        </variablelist>
2037       
2038      </sect3>
2039     
2040      <sect3 id="plugin_developer.import.autodetect.callsequence">
2041        <title>Call sequence during autodetection</title>
2042       
2043        <para>
2044          The call sequence for autodetection resembles the call sequence for
2045          checking if the plug-in can be used in a given context.
2046        </para>
2047
2048        <orderedlist>
2049        <listitem>
2050          <para>
2051          A new instance of the plug-in class is created. The plug-in must
2052          have a public no-argument constructor.
2053          </para>
2054        </listitem>
2055       
2056        <listitem>
2057          <para>
2058          The <methodname>Plugin.init()</methodname> method is called.
2059          The <varname>job</varname> parameter is <constant>null</constant>.
2060          The <varname>configuration</varname> parameter is <constant>null</constant>
2061          if the plug-in does not have any configuration parameters.
2062          </para>
2063        </listitem>
2064       
2065        <listitem>
2066          <para>
2067          If the plug-in is interactive the the <methodname>InteractivePlugin.isInContext()</methodname>
2068          is called. If the context is a list context, the <varname>item</varname>
2069          parameter is null, otherwise the current item is passed. The plug-in
2070          should return <constant>null</constant> if it can be used under the
2071          current circumstances, or a message explaining why not.
2072          </para>
2073        </listitem>
2074       
2075        <listitem>
2076          <para>
2077          If the plug-in can be used the <methodname>AutoDetectingImporter.isImportable()</methodname>
2078          method is called to check if the selected file is importable or not.
2079          </para>
2080        </listitem>
2081       
2082        <listitem>
2083          <para>
2084          After this, <methodname>Plugin.done()</methodname> is called and
2085          the plug-in instance is discarded. If there are
2086          several configurations for a plug-in, this procedure is repeated
2087          for each configuration. If the plug-in can be used without
2088          a configuration the procedure is also repeated without
2089          configuration parameters.
2090          </para>
2091        </listitem>
2092       
2093        <listitem>
2094          <para>
2095          If a single plug-in was found the user is taken to the regular
2096          job configuration wizard. A new plug-in instance is created for
2097          this. If more than one plug-in was found the user is presented
2098          with a list of the plug-ins. After selecting one of them the
2099          regular job configuration wizard is used with a new plug-in instance.
2100          </para>
2101        </listitem>
2102       
2103        </orderedlist>
2104       
2105      </sect3>
2106
2107    </sect2>
2108   
2109    <sect2 id="plugin_developer.import.abstractflatfileimporter">
2110      <title>The AbstractFlatFileImporter superclass</title>
2111      <para>
2112        The <classname docapi="net.sf.basedb.plugins">AbstractFlatFileImporter</classname> is a very useful abstract
2113        class to use as a superclass for your own import plug-ins. It can be used
2114        if your plug-in uses regular text files that can be parsed by an instance of the
2115        <classname docapi="net.sf.basedb.util">net.sf.basedb.util.FlatFileParser</classname> class. This class parses a file
2116        by checking each line against a few regular expressions. Depending on which regular
2117        expression matches the line, it is classified as a header line, a section line,
2118        a comment, a data line, a footer line or unknown. Header lines are inspected as a group,
2119        but data lines individually, meaning that it consumes very little memory since only
2120        a few lines at a time needs to be loaded.
2121      </para>
2122     
2123      <para>
2124        The <classname docapi="net.sf.basedb.plugins">AbstractFlatFileImporter</classname> defines
2125        <classname docapi="net.sf.basedb.core">PluginParameter</classname> objects
2126        for each of the regular expressions and other parameters used by the parser. It also
2127        implements the <methodname>Plugin.run()</methodname> method and does most of
2128        the ground work for instantiating a <methodname>FlatFileParser</methodname> and
2129        parsing the file. What you have to do in your plugin is to put together the
2130        <classname docapi="net.sf.basedb.core">RequestInformation</classname> objects
2131        for configuring the plugin and creating a job and implement the
2132        <methodname>InteractivePlugin.configure()</methodname> method for validating and
2133        storing the parameters. You should also implement or override some methods
2134        defined by <classname>AbstractFlatFileImporter</classname>.
2135      </para>
2136
2137      <para>
2138      Here is what you need to do:
2139      </para>
2140
2141      <itemizedlist> 
2142      <listitem>
2143        <para>
2144        Implement the <interfacename docapi="net.sf.basedb.core.plugin">InteractivePlugin</interfacename> methods.
2145        See <xref linkend="plugin_developer.api.interfaces.interactive" /> for more information. Note that the
2146        <classname docapi="net.sf.basedb.plugins">AbstractFlatFileImporter</classname>
2147        has defined many parameters for regular expressions used by the parser
2148        already. You should just pick them and put in your <classname docapi="net.sf.basedb.core">RequestInformation</classname>
2149        object.
2150        </para>
2151       
2152        <programlisting language="java">
2153// Parameter that maps the items name from a column
2154private PluginParameter&lt;String&gt; nameColumnMapping;
2155
2156// Parameter that maps the items description from a column
2157private PluginParameter&lt;String&gt; descriptionColumnMapping;
2158
2159private RequestInformation getConfigurePluginParameters(GuiContext context)
2160{
2161   if (configurePlugin == null)
2162   {
2163      // To store parameters for CONFIGURE_PLUGIN
2164      List&lt;PluginParameter&lt;?&gt;&gt; parameters =
2165         new ArrayList&lt;PluginParameter&lt;?&gt;&gt;();
2166
2167      // Parser regular expressions - from AbstractFlatFileParser
2168      parameters.add(parserSection);
2169      parameters.add(headerRegexpParameter);
2170      parameters.add(dataHeaderRegexpParameter);
2171      parameters.add(dataSplitterRegexpParameter);
2172      parameters.add(ignoreRegexpParameter);
2173      parameters.add(dataFooterRegexpParameter);
2174      parameters.add(minDataColumnsParameter);
2175      parameters.add(maxDataColumnsParameter);
2176
2177      // Column mappings
2178      nameColumnMapping = new PluginParameter&lt;String&gt;(
2179         "nameColumnMapping",
2180         "Name",
2181         "Mapping that picks the items name from the data columns",
2182         new StringParameterType(255, null, true)
2183      );
2184   
2185      descriptionColumnMapping = new PluginParameter&lt;String&gt;(
2186        "descriptionColumnMapping",
2187        "Description",
2188        "Mapping that picks the items description from the data columns",
2189        new StringParameterType(255, null, false)
2190      );
2191
2192      parameters.add(mappingSection);
2193      parameters.add(nameColumnMapping);
2194      parameters.add(descriptionColumnMapping);
2195     
2196      configurePlugin = new RequestInformation
2197      (
2198         Request.COMMAND_CONFIGURE_PLUGIN,
2199         "File parser settings",
2200         "",
2201         parameters
2202      );
2203
2204   }
2205   return configurePlugin;
2206}
2207</programlisting>
2208      </listitem>
2209     
2210      <listitem>
2211        <para>
2212        Implement/override some of the methods defined by
2213        <classname>AbstractFlatFileParser</classname>. The most important
2214        methods are listed below.
2215        </para>
2216      </listitem>
2217     
2218      </itemizedlist>
2219
2220      <variablelist>
2221      <varlistentry>
2222        <term>
2223          <methodsynopsis language="java">
2224            <modifier>protected</modifier>
2225            <type>FlatFileParser</type>
2226            <methodname>getInitializedFlatFileParser</methodname>
2227            <exceptionname>BaseException</exceptionname>
2228          </methodsynopsis>
2229        </term>
2230        <listitem>
2231          <para>
2232          The method is called to create a <classname docapi="net.sf.basedb.util.parser">FlatFileParser</classname>
2233          and set the regular expressions that should be used for parsing the file.
2234          The default implementation assumes that your plug-in has used the built-in
2235          <classname docapi="net.sf.basedb.core">PluginParameter</classname> objects and has stored the values
2236          at the configuration level. You should override this method if you need to
2237          initialise the parser in a different way. See for example the
2238          code for the <classname docapi="net.sf.basedb.plugins">PrintMapFlatFileImporter</classname> plug-in which
2239          has a fixed format and doesn't use configurations.
2240          </para>
2241          <programlisting language="java">
2242@Override
2243protected FlatFileParser getInitializedFlatFileParser()
2244   throws BaseException
2245{
2246   FlatFileParser ffp = new FlatFileParser();
2247   ffp.setSectionRegexp(Pattern.compile("\\[(.+)\\]"));
2248   ffp.setHeaderRegexp(Pattern.compile("(.+)=,(.*)"));
2249   ffp.setDataSplitterRegexp(Pattern.compile(","));
2250   ffp.setDataFooterRegexp(Pattern.compile(""));
2251   ffp.setMinDataColumns(12);
2252   return ffp;
2253}
2254</programlisting>
2255        </listitem>
2256      </varlistentry>
2257     
2258      <varlistentry>
2259        <term>
2260          <methodsynopsis language="java">
2261            <modifier>protected</modifier>
2262            <type>boolean</type>
2263            <methodname>isImportable</methodname>
2264            <methodparam>
2265              <type>FlatFileParser</type>
2266              <parameter>ffp</parameter>
2267            </methodparam>
2268            <exceptionname>IOException</exceptionname>
2269          </methodsynopsis>
2270        </term>
2271        <listitem>
2272          <para>
2273          This method is called from the <methodname>isImportable(InputStream)</methodname>
2274          method, AFTER <methodname>FlatFileParser.nextSection()</methodname> and
2275          <methodname>FlatFileParser.parseHeaders()</methodname> has been called
2276          a single time and if the <methodname>parseHeaders</methodname> method didn't
2277          stop on an unknown line. The default implementation of this method always returns
2278          TRUE, since obviously some data has been found. A subclass may override this method
2279          if it wants to do more checks, for example, make that a certain header is present
2280          with a certain value. It may also continue parsing the file. Here is a code example from
2281          the <classname docapi="net.sf.basedb.plugins">PrintMapFlatFileImporter</classname> which checks if a
2282          <constant>FormatName</constant> header is present and contains either
2283          <constant>TAM</constant> or <constant>MwBr</constant>.
2284          </para>
2285         
2286          <programlisting language="java">
2287/**
2288   Check that the file is a TAM or MwBr file.
2289   @return TRUE if a FormatName header is present and contains "TAM" or "MwBr", FALSE
2290      otherwise
2291*/
2292@Override
2293protected boolean isImportable(FlatFileParser ffp)
2294{
2295   String formatName = ffp.getHeader("FormatName");
2296   return formatName != null &amp;&amp; 
2297      (formatName.contains("TAM") || formatName.contains("MwBr"));
2298}
2299</programlisting>
2300        </listitem>
2301      </varlistentry>
2302     
2303      <varlistentry>
2304        <term>
2305          <methodsynopsis language="java">
2306            <modifier>protected</modifier>
2307            <void/>
2308            <methodname>begin</methodname>
2309            <methodparam>
2310              <type>FlatFileParser</type>
2311              <parameter>ffp</parameter>
2312            </methodparam>
2313            <exceptionname>BaseException</exceptionname>
2314          </methodsynopsis>
2315        </term>
2316        <listitem>
2317          <para>
2318          This method is called just before the parsing of the file
2319          begins. Override this method if you need to initialise some
2320          internal state. This is, for example, a good place to open
2321          a <classname docapi="net.sf.basedb.core">DbControl</classname> object, read parameters from the
2322          job and configuration and put them into more useful variables. The default
2323          implementation does nothing, but we recommend that
2324          <methodname>super.begin()</methodname> is always called.
2325          </para>
2326          <programlisting language="java">
2327// Snippets from the RawDataFlatFileImporter class
2328private DbControl dc;
2329private RawDataBatcher batcher;
2330private RawBioAssay rawBioAssay;
2331private Map&lt;String, String&gt; columnMappings;
2332private int numInserted;
2333
2334@Override
2335protected void begin()
2336   throws BaseException
2337{
2338   super.begin();
2339
2340   // Get DbControl
2341   dc = sc.newDbControl();
2342   rawBioAssay = (RawBioAssay)job.getValue(rawBioAssayParameter.getName());
2343
2344   // Reload raw bioassay using current DbControl
2345   rawBioAssay = RawBioAssay.getById(dc, rawBioAssay.getId());
2346   
2347   // Create a batcher for inserting spots
2348   batcher = rawBioAssay.getRawDataBatcher();
2349
2350   // For progress reporting
2351   numInserted = 0;
2352}         
2353</programlisting>
2354        </listitem>
2355      </varlistentry>
2356      <varlistentry>
2357        <term>
2358          <methodsynopsis language="java">
2359            <modifier>protected</modifier>
2360            <void/>
2361            <methodname>handleHeader</methodname>
2362            <methodparam>
2363              <type>FlatFileParser.Line</type>
2364              <parameter>line</parameter>
2365            </methodparam>
2366            <exceptionname>BaseException</exceptionname>
2367          </methodsynopsis>
2368        </term>
2369        <listitem>
2370          <para>
2371          This method is called once for every header line that is found in
2372          the file. The <varname>line</varname> parameter contains information
2373          about the header. The default implementation of this method does
2374          nothing.
2375          </para>
2376          <programlisting language="java">
2377@Override
2378protected void handleHeader(Line line)
2379   throws BaseException
2380{
2381   super.handleHeader(line);
2382   if (line.name() != null &amp;&amp; line.value() != null)
2383   {
2384      rawBioAssay.setHeader(line.name(), line.value());
2385   }
2386}
2387</programlisting>
2388        </listitem>
2389      </varlistentry>
2390      <varlistentry>
2391        <term>
2392          <methodsynopsis language="java">
2393            <modifier>protected</modifier>
2394            <void/>
2395            <methodname>handleSection</methodname>
2396            <methodparam>
2397              <type>FlatFileParser.Line</type>
2398              <parameter>line</parameter>
2399            </methodparam>
2400            <exceptionname>BaseException</exceptionname>
2401          </methodsynopsis>
2402        </term>
2403        <listitem>
2404          <para>
2405            This method is called once for each section that is found in the file.
2406            The <varname>line</varname> parameter contains information
2407            about the section. The default implementation of this method does
2408            nothing.
2409          </para>
2410        </listitem>
2411      </varlistentry>
2412     
2413      <varlistentry>
2414        <term>
2415          <methodsynopsis language="java">
2416            <modifier>protected abstract</modifier>
2417            <void/>
2418            <methodname>beginData</methodname>
2419            <exceptionname>BaseException</exceptionname>
2420          </methodsynopsis>
2421        </term>
2422        <listitem>
2423          <para>
2424          This method is called after the headers has been parsed, but before
2425          the first line of data. This is a good place to add code that
2426          depends on information in the headers, for example, put
2427          together column mappings.
2428          </para>
2429         
2430          <programlisting language="java">
2431private Mapper reporterMapper;
2432private Mapper blockMapper;
2433private Mapper columnMapper;
2434private Mapper rowMapper;
2435// ... more mappers
2436
2437@Override
2438protected void beginData()
2439{
2440   boolean cropStrings = ("crop".equals(job.getValue("stringTooLongError")));
2441
2442   // Mapper that always return null; used if no mapping expression has been entered
2443   Mapper nullMapper = new ConstantMapper((String)null);
2444   
2445   // Column mappers
2446   reporterMapper = getMapper(ffp, (String)configuration.getValue("reporterIdColumnMapping"),
2447      cropStrings ? ReporterData.MAX_EXTERNAL_ID_LENGTH : null, nullMapper);
2448   blockMapper = getMapper(ffp, (String)configuration.getValue("blockColumnMapping"),
2449      null, nullMapper);
2450   columnMapper = getMapper(ffp, (String)configuration.getValue("columnColumnMapping"),
2451      null, nullMapper);
2452   rowMapper = getMapper(ffp, (String)configuration.getValue("rowColumnMapping"),
2453      null, nullMapper);
2454   // ... more mappers: metaGrid coordinate, X-Y coordinate, extended properties
2455   // ...
2456}
2457</programlisting>
2458         
2459        </listitem>
2460      </varlistentry>
2461       
2462      <varlistentry>
2463        <term>
2464          <methodsynopsis language="java">
2465            <modifier>protected abstract</modifier>
2466            <void/>
2467            <methodname>handleData</methodname>
2468            <methodparam>
2469              <type>FlatFileParser.Data</type>
2470              <parameter>data</parameter>
2471            </methodparam>
2472            <exceptionname>BaseException</exceptionname>
2473          </methodsynopsis>
2474        </term>
2475        <listitem>
2476          <para>
2477          This method is abstract and must be implemented by all subclasses.
2478          It is called once for every data line in the the file.
2479          </para>
2480
2481          <programlisting language="java">
2482// Snippets from the RawDataFlatFileImporter class
2483@Override
2484protected void handleData(Data data)
2485   throws BaseException
2486{
2487   // Create new RawData object
2488   RawData raw = batcher.newRawData();
2489
2490   // External ID for the reporter
2491   String externalId = reporterMapper.getValue(data);
2492   
2493   // Block, row and column numbers
2494   raw.setBlock(blockMapper.getInt(data));
2495   raw.setColumn(columnMapper.getInt(data));
2496   raw.setRow(rowMapper.getInt(data));
2497   // ... more: metaGrid coordinate, X-Y coordinate, extended properties
2498   
2499   // Insert raw data to the database
2500   batcher.insert(raw, externalId);
2501   numInserted++;
2502}
2503</programlisting> 
2504         
2505        </listitem>
2506      </varlistentry>
2507     
2508      <varlistentry>
2509        <term>
2510          <methodsynopsis language="java">
2511            <modifier>protected</modifier>
2512            <void/>
2513            <methodname>end</methodname>
2514            <methodparam>
2515              <type>boolean</type>
2516              <parameter>success</parameter>
2517            </methodparam>
2518          </methodsynopsis>
2519        </term>
2520        <listitem>
2521          <para>
2522            Called when the parsing has ended, either because the end of
2523            file was reached or because an error has occurred. The subclass
2524            should close any open resources, ie. the <classname docapi="net.sf.basedb.core">DbControl</classname>
2525            object. The <varname>success</varname> parameter is <constant>true</constant>
2526            if the parsing was successful, <constant>false</constant> otherwise.
2527            The default implementation does nothing.
2528          </para>
2529         
2530          <programlisting language="java">
2531@Override
2532protected void end(boolean success)
2533   throws BaseException
2534{
2535   try
2536   {
2537      // Commit if the parsing was successful
2538      if (success)
2539      {
2540         batcher.close();
2541         dc.commit();
2542      }
2543   }
2544   catch (BaseException ex)
2545   {
2546      // Well, now we got an exception
2547      success = false;
2548      throw ex;
2549   }
2550   finally
2551   {
2552      // Always close... and call super.end()
2553      if (dc != null) dc.close();
2554      super.end(success);
2555   }
2556}     
2557</programlisting>         
2558        </listitem>
2559      </varlistentry>
2560     
2561      <varlistentry>
2562        <term>
2563          <methodsynopsis language="java">
2564            <modifier>protected</modifier>
2565            <type>String</type>
2566            <methodname>getSuccessMessage</methodname>
2567            <void/>
2568          </methodsynopsis>
2569        </term>
2570        <listitem>
2571          <para>
2572          This is the last method that is called, and it is only called if
2573          everything went suceessfully. This method allows a subclass to generate
2574          a short message that is sent back to the database as a final progress
2575          report. The default implementation returns null, which means that no
2576          message will be generated.
2577          </para>
2578          <programlisting language="java">
2579@Override
2580protected String getSuccessMessage()
2581{
2582   return numInserted + " spots inserted";
2583}
2584</programlisting>
2585        </listitem>
2586      </varlistentry>
2587      </variablelist>
2588
2589      <para>
2590        The <classname docapi="net.sf.basedb.plugins">AbstractFlatFileImporter</classname> has a lot of
2591        other methods that you may use and/or override in your own plug-in.
2592        Check the javadoc for more information.
2593      </para>
2594
2595      <sect3 id="plugin_developer.import.configurebyexample">
2596        <title>Configure by example</title>
2597       
2598        <para>
2599          The <interfacename docapi="net.sf.basedb.util.parser">ConfigureByExample</interfacename> 
2600          is a tagging interface that can be used by plug-ins using the
2601          <classname docapi="net.sf.basedb.util.parser">FlatFileParser</classname> class for parsing.
2602          The web client detects if a plug-in implements this interface and if the list of
2603          parameters includes a section parameter with the name <constant>parserSection</constant>
2604          a <guibutton>Test with file</guibutton> buttons is activated. This button will take the
2605          user to a form which allows the user to enter values for the parameters defined in
2606          the <classname>AbstractFlatFileImporter</classname> class. Parameters for column mappings
2607          must have the string "Mapping" in their names.
2608        </para>
2609      </sect3>
2610    </sect2>
2611   
2612  </sect1>
2613
2614  <sect1 id="plugin_developer.export">
2615    <?dbhtml filename="export.html" ?>
2616    <title>Export plug-ins</title>
2617   
2618    <para>
2619      Export plug-ins are plug-ins that takes data from BASE, and
2620      prepares it for use with some external entity. Usually this
2621      means that data is taken from the database and put into a file
2622      with some well-defined file format.
2623      An export plug-in should return <constant>MainType.EXPORT</constant> from
2624      the <methodname>Plugin.getMainType()</methodname> method.
2625    </para>
2626   
2627    <sect2 id="plugin_developer.export.download">
2628      <title>Immediate download of exported data</title>
2629      <para>
2630        An export plug-in may want to give the user a choice
2631        between saving the exported data in the BASE file system
2632        or to download it immediately to the client computer. With the basic
2633        plug-in API the second option is not possible. The
2634        <interfacename docapi="net.sf.basedb.core.plugin">ImmediateDownloadExporter</interfacename> is an
2635        interface that extends the <interfacename docapi="net.sf.basedb.core.plugin">Plugin</interfacename>
2636        interface to provide this functionality. If your export
2637        plug-in wants to provide immediate download functionality it must
2638        implement the <interfacename docapi="net.sf.basedb.core.plugin">ImmediateDownloadExporter</interfacename>
2639        interface.
2640      </para>
2641     
2642      <sect3 id="plugin_developer.export.immediatedownloadexporter">
2643      <title>The ImmediateDownloadExporter interface</title>
2644      <variablelist>
2645      <varlistentry>
2646        <term>
2647          <methodsynopsis language="java">
2648            <modifier>public</modifier>
2649            <void/>
2650            <methodname>doExport</methodname>
2651            <methodparam>
2652              <type>ExportOutputStream</type>
2653              <parameter>out</parameter>
2654            </methodparam>
2655            <methodparam>
2656              <type>ProgressReporter</type>
2657              <parameter>progress</parameter>
2658            </methodparam>
2659          </methodsynopsis>
2660        </term>
2661        <listitem>
2662          <para>
2663          Perform the export. The plug-in should write the
2664          exported data to the <varname>out</varname> stream.
2665          If the <varname>progress</varname> parameter is not null,
2666          the progress should be reported at regular interval in the
2667          same manner as in the <methodname>Plugin.run()</methodname>
2668          method.
2669          </para>
2670        </listitem>
2671      </varlistentry>
2672      </variablelist>
2673     
2674      </sect3>
2675     
2676      <sect3 id="plugin_developer.export.exportoutputstream">
2677      <title>The ExportOutputStream class</title>
2678     
2679      <para>
2680        The <classname docapi="net.sf.basedb.core.plugin">ExportOutputStream</classname> is
2681        an extension to the
2682        <classname>java.io.OutputStream</classname>. Use the regular
2683        <methodname>write()</methodname> methods to write data to it.
2684        It also has some additional methods, which are used for setting
2685        metadata about the generated file. These methods are useful, for
2686        example, when generating HTTP response headers.
2687      </para>
2688     
2689      <note>
2690        <para>
2691        These methods must be called before starting to write data to
2692        the <varname>out</varname> stream.
2693        </para>
2694      </note>
2695     
2696      <variablelist>
2697      <varlistentry>
2698        <term>
2699          <methodsynopsis language="java">
2700            <modifier>public</modifier>
2701            <void/>
2702            <methodname>setContentLength</methodname>
2703            <methodparam>
2704              <type>long</type>
2705              <parameter>contentLength</parameter>
2706            </methodparam>
2707          </methodsynopsis>
2708        </term>
2709        <listitem>
2710          <para>
2711          Set the total size of the exported data. Don't call this method if the
2712          total size is not known.
2713          </para>
2714        </listitem>
2715      </varlistentry>
2716      <varlistentry>
2717        <term>
2718          <methodsynopsis language="java">
2719            <modifier>public</modifier>
2720            <void/>
2721            <methodname>setMimeType</methodname>
2722            <methodparam>
2723              <type>String</type>
2724              <parameter>mimeType</parameter>
2725            </methodparam>
2726          </methodsynopsis>
2727        </term>
2728        <listitem>
2729          <para>
2730          Set the MIME type of the file that is being generated.
2731          </para>
2732        </listitem>
2733      </varlistentry>
2734      <varlistentry>
2735        <term>
2736          <methodsynopsis language="java">
2737            <modifier>public</modifier>
2738            <void/>
2739            <methodname>setCharacterSet</methodname>
2740            <methodparam>
2741              <type>String</type>
2742              <parameter>charset</parameter>
2743            </methodparam>
2744          </methodsynopsis>
2745        </term>
2746        <listitem>
2747          <para>
2748          Sets the character set used in text files. For example,
2749          UTF-8 or ISO-8859-1.
2750          </para>
2751        </listitem>
2752      </varlistentry>
2753      <varlistentry>
2754        <term>
2755          <methodsynopsis language="java">
2756            <modifier>public</modifier>
2757            <void/>
2758            <methodname>setFilename</methodname>
2759            <methodparam>
2760              <type>String</type>
2761              <parameter>filename</parameter>
2762            </methodparam>
2763          </methodsynopsis>
2764        </term>
2765        <listitem>
2766          <para>
2767          Set a suggested name of the file that is being
2768          generated.
2769          </para>
2770        </listitem>
2771      </varlistentry>
2772      </variablelist>
2773     
2774      </sect3>
2775     
2776      <sect3 id="plugin_developer.export.callsequence">
2777        <title>Call sequence during immediate download</title>
2778
2779      <para>
2780        Supporting immediate download also means that the method call
2781        sequence is a bit altered from the standard sequence described
2782        in <xref linkend="plugin_developer.api.callsequence.execute" />.
2783      </para>
2784     
2785      <itemizedlist>
2786      <listitem>
2787        <para>
2788        The plug-in must call <methodname>Response.setDownloadImmediately()</methodname>
2789        instead of <methodname>Response.setDone()</methodname> in <methodname>Plugin.configure()</methodname>
2790        to end the job configuration wizard. This requests that the core starts
2791        an immediate download.
2792        </para>
2793       
2794        <note>
2795          <para>
2796          Even if an immediate download is requested by the plug-in this feature
2797          may have been disabled by the server administrator. If so, the plug-in
2798          can choose if the job should be added to job queue or if this is an
2799          error condition.
2800          </para>
2801        </note>
2802      </listitem>
2803     
2804      <listitem>
2805        <para>
2806        If immediate download is granted the web client will keep the
2807        same plug-in instance and call <methodname>ImmediateDownloadExporter.doExport()</methodname>.
2808        In this case, the <methodname>Plugin.run()</methodname> is never called.
2809        After the export, <methodname>Plugin.done()</methodname> is called as
2810        usual.
2811        </para>
2812       
2813      </listitem>
2814
2815      <listitem>
2816        <para>
2817        If immediate download is not granted and the job is added to the job queue
2818        the regular job execution sequence is used.
2819        </para>
2820      </listitem>
2821      </itemizedlist>
2822     
2823      </sect3>
2824
2825    </sect2>
2826   
2827    <sect2 id="plugin_developer.export.abstractexporter">
2828      <title>The AbstractExporterPlugin class</title>
2829   
2830      <para>
2831        This is an abstract superclass that will make it easier
2832        to implement export plug-ins that support immediate
2833        download. It defines <classname docapi="net.sf.basedb.core">PluginParameter</classname>
2834        objects for asking a user about a path where the exported
2835        data should be saved and if existing files should be overwritten or not.
2836        If the user leaves the path empty the immediate download functionality
2837        should be used. It also contains implementations of both the
2838        <methodname>Plugin.run()</methodname> method and the
2839        <methodname>ImmediateDownloadExporter.doExport()</methodname> method.
2840        Here is what you need to do in your own plug-in code (code examples are
2841        taken from the <classname docapi="net.sf.basedb.plugins">HelpExporter</classname>):
2842      </para>
2843     
2844      <itemizedlist>
2845      <listitem>
2846        <para>
2847          Your plug-in should extend the <classname docapi="net.sf.basedb.core.plugin">AbstractExporterPlugin</classname>
2848          class:
2849          <programlisting language="java">
2850public class HelpExporter
2851  extends AbstractExporterPlugin
2852  implements InteractivePlugin
2853</programlisting>
2854        </para>
2855      </listitem>
2856     
2857      <listitem>
2858        <para>
2859          You need to implement the
2860          <methodname>InteractivePlugin.getRequestInformation()</methodname>
2861          method. Use the <methodname>getSaveAsParameter()</methodname>
2862          and <methodname>getOverwriteParameter()</methodname> methods defined in the
2863          superclass to create plug-in parameters that asks for the file name to save
2864          to and if existing files can be overwritten or not.
2865          You should also check if the administrator has enabled the immediate execution
2866          functionality for your plug-in. If not, the only option is to
2867          export to a file in the BASE file system and the filename is a
2868          required parameter.
2869         
2870          <programlisting language="java">
2871// Selected parts of the getRequestConfiguration() method
2872...
2873List&lt;PluginParameter&lt;?&gt;&gt; parameters =
2874   new ArrayList&lt;PluginParameter&lt;?&gt;&gt;();
2875...
2876PluginDefinition pd = job.getPluginDefinition();
2877boolean requireFile = pd == null ?
2878   false : !pd.getAllowImmediateExecution();
2879
2880parameters.add(getSaveAsParameter(null, null, defaultPath, requireFile));
2881parameters.add(getOverwriteParameter(null, null));
2882
2883configureJob = new RequestInformation
2884(
2885   Request.COMMAND_CONFIGURE_JOB,
2886   "Help exporter options",
2887   "Set Client that owns the helptexts, " +
2888     "the file path where the export file should be saved",
2889   parameters
2890);
2891....
2892return configureJob;
2893</programlisting>
2894        </para>
2895      </listitem>
2896
2897      <listitem>
2898        <para>
2899        You must also implement the <methodname>configure()</methodname>
2900        method and check the parameters. If no filename has been given,
2901        you should check if immediate exection is allowed and set an
2902        error if it is not. If a filename is present, use the
2903        <methodname>pathCanBeUsed()</methodname> method to check if
2904        it is possible to save the data to a file with that name. If the
2905        file already exists it can be overwritten if the <varname>OVERWRITE</varname>
2906        is <constant>TRUE</constant> or if the file has been flagged for removal.
2907        Do not forget to store the parameters with the <methodname>storeValue()</methodname>
2908        method.
2909
2910        <programlisting language="java">
2911// Selected parts from the configure() method
2912if (request.getParameterValue(SAVE_AS) == null)
2913{
2914   if (!request.isAllowedImmediateExecution())
2915   {
2916      response.setError("Immediate download is not allowed. " +
2917         "Please specify a filename.", null);
2918      return;
2919   }
2920   Client client = (Client)request.getParameterValue("client");
2921   response.setDownloadImmediately("Export help texts for client application " +
2922     client.getName(), ExecutionTime.SHORTEST, true);
2923}
2924else
2925{
2926   if (!pathCanBeUsed((String)request.getParameterValue(SAVE_AS),
2927      (Boolean)request.getParameterValue(OVERWRITE)))
2928   {
2929      response.setError("File exists: " +
2930         (String)request.getParameterValue(SAVE_AS), null);
2931      return;
2932   }
2933   storeValue(job, request, ri.getParameter(SAVE_AS));
2934   storeValue(job, request, ri.getParameter(OVERWRITE));
2935   response.setDone("The job configuration is complete", ExecutionTime.SHORTEST);
2936}
2937</programlisting>
2938       
2939        </para>
2940      </listitem>
2941     
2942      <listitem>
2943        <para>
2944        Implement the <methodname>performExport()</methodname> method.
2945        This is defined as abstract in the <classname docapi="net.sf.basedb.core.plugin">AbstractExporterPlugin</classname>
2946        class. It has the same parameters as the <methodname>ImmediateDownloadExporter.doExport()</methodname>
2947        method and they have the same meaning. The only difference is that the
2948        <varname>out</varname> stream can be linked to a file in the BASE filesystem
2949        and not just to the HTTP response stream.
2950        </para>
2951      </listitem>
2952     
2953      <listitem>
2954        <para>
2955        Optionally, implement the <methodname>begin()</methodname>,
2956        <methodname>end()</methodname> and <methodname>getSuccessMessage()</methodname>
2957        methods. Theese methods do nothing by default.
2958        </para>
2959      </listitem>
2960      </itemizedlist>
2961     
2962      <para>
2963        The call sequence for plug-ins extending <classname docapi="net.sf.basedb.core.plugin">AbstractExporterPlugin</classname>
2964        is:
2965      </para>
2966     
2967      <orderedlist>
2968      <listitem>
2969        <para>
2970        Call <methodname>begin()</methodname>.
2971        </para>
2972      </listitem>
2973      <listitem>
2974        <para>
2975        Call <methodname>performExport()</methodname>.
2976        </para>
2977      </listitem>
2978      <listitem>
2979        <para>
2980        Call <methodname>end()</methodname>.
2981        </para>
2982      </listitem>
2983      <listitem>
2984        <para>
2985        Call <methodname>getSuccessMessage()</methodname> if running as a regular
2986        job. This method is never called when doing an immediate download since there
2987        is no place to show the message.
2988        </para>
2989      </listitem>
2990      </orderedlist>
2991     
2992    </sect2>
2993   
2994  </sect1>
2995 
2996  <sect1 id="plugin_developer.analyse">
2997    <?dbhtml filename="analysis.html" ?>
2998    <title>Analysis plug-ins</title>
2999    <para>
3000      A plug-in becomes an analysis plug-in simply by returning
3001      <constant>Plugin.MainType.ANALYZE</constant> from the
3002      <methodname>Plugin.getMainType()</methodname> method. The information returned from
3003      <methodname>InteractivePlugin.getGuiContexts()</methodname>
3004      should include: [<constant>Item.BIOASSAYSET</constant>, <constant>Type.ITEM</constant>]
3005      or [<constant>Item.DERIVEDBIOASSAYSET</constant>, <constant>Type.ITEM</constant>]
3006      since web client doesn't look for analysis plug-ins in most other places. If
3007      the plug-in can work on a subset of the bioassays it may also include
3008      [<constant>Item.BIOASSAY</constant>, <constant>Type.LIST</constant>]
3009      among the contexts. This will make it possible for a user to select
3010      bioassays from the list and then invoke the plug-in. The following
3011      code examples are taken from an analysis plug-in that is used in an experiment
3012      context.
3013    </para>
3014   
3015<programlisting language="java">
3016private static final Set&lt;GuiContext&gt; guiContexts =
3017   Collections.singleton(new GuiContext(Item.BIOASSAYSET, GuiContext.Type.ITEM));
3018
3019public Set&lt;GuiContext&gt; getGuiContexts()
3020{
3021   return guiContexts;
3022}</programlisting>
3023
3024    <para>
3025    If the plug-in depends on a specific raw data type or on the number of
3026    channels, it should check that the current bioassayset is of the
3027    correct type in the <methodname>InteractivePlugin.isInContext()</methodname> 
3028    method. It is also a good idea to check if the current user has permission
3029    to use the current experiment. This permission is needed to create new bioassaysets or
3030    other data belonging to the experiment.
3031    </para>
3032 
3033    <programlisting language="java">
3034public boolean isInContext(GuiContext context, Object item)
3035{
3036   if (item == null)
3037   {
3038      message = "The object is null";
3039   }
3040   else if (!(item instanceof BioAssaySet))
3041   {
3042      message = "The object is not a BioAssaySet: " + item;
3043   }
3044   else
3045   {
3046      BioAssaySet bas = (BioAssaySet)item;
3047      int channels = bas.getRawDataType().getChannels();
3048      if (channels != 2)
3049      {
3050         message = "This plug-in requires 2-channel data, not " + channels + "-channel.";
3051      }
3052      else
3053      {
3054         Experiment e = bas.getExperiment();
3055         e.checkPermission(Permission.USE);
3056      }
3057   }
3058}
3059</programlisting>
3060
3061    <para>
3062    The plug-in should always include a parameter asking for the current
3063    bioassay set when the <methodname>InteractivePlugin.getRequestInformation()</methodname>
3064    is called with <literal>command = Request.COMMAND_CONFIGURE_JOB</literal>.
3065    </para> 
3066
3067    <programlisting language="java">
3068private static final RequestInformation configurePlugin;
3069private RequestInformation configureJob;
3070private PluginParameter&lt;BioAssaySet&gt; bioAssaySetParameter;
3071
3072public RequestInformation getRequestInformation(GuiContext context, String command)
3073   throws BaseException
3074{
3075   RequestInformation requestInformation = null;
3076   if (command.equals(Request.COMMAND_CONFIGURE_PLUGIN))
3077   {
3078      requestInformation = getConfigurePlugin(context);
3079   }
3080   else if (command.equals(Request.COMMAND_CONFIGURE_JOB))
3081   {
3082      requestInformation = getConfigureJob(context);
3083   }
3084   return requestInformation;
3085}
3086
3087private RequestInformation getConfigureJob(GuiContext context)
3088{
3089   if (configureJob == null)
3090   {
3091      bioAssaySetParameter; = new PluginParameter&lt;BioAssaySet&gt;(
3092         "bioAssaySet",
3093         "Bioassay set",
3094         "The bioassay set used as the source for this analysis plugin",
3095         new ItemParameterType&lt;BioAssaySet&gt;(BioAssaySet.class, null, true, 1, null)
3096      );
3097
3098      List&lt;PluginParameter&lt;?&gt;&gt; parameters = new ArrayList&lt;PluginParameter&lt;?&gt;&gt;();
3099      parameters.add(bioAssaySetParameter);
3100      // Add more plug-in-specific parameters here...
3101   
3102      configureJob = new RequestInformation(
3103         Request.COMMAND_CONFIGURE_JOB,
3104         "Configure job",
3105         "Set parameter for plug-in execution",
3106         parameters
3107      );
3108   }
3109   return configureJob;
3110}
3111</programlisting>
3112
3113    <para>
3114    Of course, the <methodname>InteractivePlugin.configure()</methodname> method needs
3115    to validate and store the bioassay set parameter as well:
3116    </para>
3117 
3118    <programlisting language="java">
3119public void configure(GuiContext context, Request request, Response response)
3120{
3121   String command = request.getCommand();
3122   try
3123   {
3124      if (command.equals(Request.COMMAND_CONFIGURE_PLUGIN))
3125      {
3126         // Validate and store configuration parameters
3127         response.setDone("Plugin configuration complete");
3128      }
3129      else if (command.equals(Request.COMMAND_CONFIGURE_JOB))
3130      {
3131         List&lt;Throwable&gt; errors =
3132            validateRequestParameters(configureJob.getParameters(), request);
3133         if (errors != null)
3134         {
3135            response.setError(errors.size() +
3136               " invalid parameter(s) were found in the request", errors);
3137            return;
3138         }
3139         storeValue(job, request, bioAssaySetParameter);
3140         // Store other plugin-specific parameters
3141     
3142         response.setDone("Job configuration complete", Job.ExecutionTime.SHORT);
3143      }
3144   }
3145   catch (Throwable ex)
3146   {
3147      // Never throw exception, always set response!
3148      response.setError(ex.getMessage(), Arrays.asList(ex));
3149   }
3150}
3151</programlisting>
3152
3153    <para>
3154    Now, the typical <methodname>Plugin.run()</methodname> method loads the specfied bioassay set
3155    and its spot data. It may do some filtering and recalculation of the spot
3156    intensity value(s). In most cases it will store the result as a child bioassay
3157    set with one bioassay for each bioassay in the parent bioassay set.
3158    Here is an example, which just copies the intensity values, while
3159    removing those with a negative value in either channel.
3160    </para>
3161 
3162    <programlisting language="java">
3163public void run(Request request, Response response, ProgressReporter progress)
3164{
3165   DbControl dc = sc.newDbControl();
3166   try
3167   {
3168      BioAssaySet source = (BioAssaySet)job.getParameter("bioAssaySet");
3169      // Reload with current DbControl
3170      source = BioAssaySet.getById(dc, source.getId());
3171      int channels = source.getRawDataType().getChannels();
3172     
3173      // Create transformation and new bioassay set
3174      Job j = Job.getById(dc, job.getId());
3175      Transformation t = source.newTransformation(j);
3176      t.setName("Copy spot intensities &gt;= 0");
3177      dc.saveItem(t);
3178
3179      BioAssaySet result = t.newProduct(null, "new", true);
3180      result.setName("After: Copying spot intensities");
3181      dc.saveItem(result);
3182
3183      // Get query for source data
3184      DynamicSpotQuery query = source.getSpotData();
3185     
3186      // Do not return spots with intensities &lt; 0
3187      for (int ch = 1; ch &lt;= channels; ++ch)
3188      {
3189         query.restrict(
3190            Restrictions.gteq(
3191               Dynamic.column(VirtualColumn.channel(ch)),
3192               Expressions.integer(0)
3193            )
3194         );
3195      }
3196     
3197      // Create batcher and copy data
3198      SpotBatcher batcher = result.getSpotBatcher();
3199      int spotsCopied = batcher.insert(query);
3200      batcher.close();
3201     
3202      // Commit and return
3203      dc.commit();
3204      response.setDone("Copied " + spotsCopied + " spots.");
3205   }
3206   catch (Throwable t)
3207   {
3208      response.setError(t.getMessage(), Arrays.asList(t));
3209   }
3210   finally
3211   {
3212      if (dc != null) dc.close();
3213   }
3214}
3215</programlisting>
3216
3217    <para>
3218    See <xref linkend="core_api.dynamic" />
3219    for more examples of using the analysis API.
3220    </para>
3221
3222    <sect2 id="plugin_developer.analyse.abstractanalysis">
3223      <title>The AbstractAnalysisPlugin class</title>
3224     
3225      <para>
3226        This class is an abstract base class. It is a useful
3227        class for most analysis plug-ins used in an experiment context
3228        to inherit from. Its main
3229        purpose is to define <classname docapi="net.sf.basedb.core">PluginParameter</classname>
3230        objects that are commonly used in analysis plug-ins. This includes:
3231      </para>
3232     
3233      <itemizedlist>
3234      <listitem>
3235        <para>
3236        The source bioassay set:
3237          <methodname>getSourceBioAssaySetParameter()</methodname>,
3238          <methodname>getCurrentBioAssaySet()</methodname>,
3239          <methodname>getSourceBioAssaySet()</methodname>
3240        </para>
3241      </listitem>
3242      <listitem>
3243        <para>
3244        The optional restriction of which bioassays to use.
3245        All bioassays in a bioassay set will be used if this
3246        parameter is empty. This is useful when the plugin only
3247        should run on a subset of bioassays in a bioassay set:
3248          <methodname>getSourceBioAssaysParameter()</methodname>,
3249          <methodname>getSourceBioAssays()</methodname>
3250        </para>
3251      </listitem>
3252      <listitem>
3253        <para>
3254        The name and description of the child bioassay set that
3255        is going to be created by the plug-in:
3256        <methodname>getChildNameParameter()</methodname>,
3257        <methodname>getChildDescriptionParameter()</methodname>
3258        </para>
3259      </listitem>
3260      <listitem>
3261        <para>
3262        The name and description of the transformation that
3263        represents the execution of the plug-in:
3264        <methodname>getTransformationNameParameter()</methodname>,
3265        <methodname>getTransformationName()</methodname>
3266        </para>
3267      </listitem>
3268      </itemizedlist>
3269     
3270    </sect2>
3271   
3272    <sect2 id="plugin_developer.analyse.filterplugin">
3273      <title>The AnalysisFilterPlugin interface</title>
3274     
3275      <para>
3276        The <interfacename docapi="net.sf.basedb.core.plugin">net.sf.basedb.core.plugin.AnalysisFilterPlugin</interfacename> 
3277        is a tagging interface, with no methods, that all analysis plug-ins that only filters
3278        data should implement. The benefit is that they will be linked from the
3279        <guibutton>Filter bioassay set</guibutton> button and not just
3280        the <guibutton>Run analysis</guibutton> button. They will also get
3281        a different icon in the experiment outline to make filtering
3282        transformations appear different from other transformations.
3283      </para>
3284     
3285      <para>
3286        The interface exists purely for making the user interaction better. There is
3287        no harm in not implementing it since the plug-in will always appear in
3288        from the <guibutton>Run analysis</guibutton> button. On the other hand,
3289        it doesn't cost anything to implement the interface since it doesn't
3290        have any methods.
3291      </para>
3292   
3293    </sect2>
3294
3295  </sect1>
3296 
3297  <sect1 id="plugin_developer.other">
3298    <?dbhtml filename="other.html" ?>
3299    <title>Other plug-ins</title>
3300   
3301    <sect2 id="plugin_developer.other.authentication">
3302      <title>Authentication plug-ins</title>
3303     
3304      <para>
3305        BASE provides a plug-in mechanism for authenticating users
3306        (validating the username and password) when they are logging in.
3307        This plug-in mechanism is not the same as the regular plug-in API.
3308        That is, you do not have worry about user interaction or implementing the
3309        <interfacename docapi="net.sf.basedb.core.plugin">Plugin</interfacename> interface.
3310      </para>
3311     
3312      <sect3 id="plugin_developer.other.authentication.internal_external">
3313        <title>Internal vs. external authentation</title>
3314     
3315        <para>
3316          BASE can authenticate users in two ways. Either it uses the internal
3317          authentication or the external authentication. With internal
3318          authentication BASE stores logins and passwords in its own database.
3319          With external authentication this is handled by some external
3320          application. Even with external authentication it is possible to
3321          let BASE cache the logins/passwords. This makes it possible to login to
3322          BASE if the external authentication server is down.
3323        </para>
3324       
3325        <note>
3326          <para>
3327          An external authentication server can only be used to grant or deny
3328          a user access to BASE. It cannot be used to give a user permissions,
3329          or put a user into groups or different roles inside BASE.
3330          </para>
3331        </note>
3332
3333        <para>
3334          The external authentication service is only used when a user logs in.
3335          Now, one or more of several things can happen:
3336         
3337          <itemizedlist>
3338          <listitem>
3339            <para>
3340              The ROOT user is logging on. Internal authentication is always
3341              used for the root user and the authenticator plug-in is never
3342              used.
3343            </para>
3344          </listitem>
3345
3346          <listitem>
3347            <para>
3348              The login is correct and the user is already known to BASE.
3349              If the plug-in supports extra information (name, email, phone, etc.)
3350              and the <property>auth.synchronize</property> setting
3351              is <constant>TRUE</constant> the extra information is copied to
3352              the BASE server.
3353            </para>
3354          </listitem>
3355         
3356          <listitem>
3357            <para>
3358              The login is correct, but the user is not known to BASE. This happens
3359              the first time a user logs in. BASE will create
3360              a new user account. If the driver supports extra information, it
3361              is copied to the BASE server (even if <property>auth.synchronize</property> 
3362              is not set). The new user account will get the default quota
3363              and be added to the all roles and groups which has been
3364              marked as <emphasis>default</emphasis>.
3365             
3366              <note>
3367              <para>
3368                Prior to BASE 2.4 it was hardcoded to add the new user to the
3369                <emphasis>Users</emphasis> role only.
3370              </para>
3371              </note>
3372            </para>
3373          </listitem>
3374
3375          <listitem>
3376            <para>
3377              If password caching is enabled, the password is copied to BASE.
3378              If an expiration timeout has been set, an expiration date
3379              will be calculated and set on the user account. The expiration
3380              date is only checked when the external authentication server is
3381              down.
3382            </para>
3383          </listitem>
3384
3385          <listitem>
3386            <para>
3387              The authentication server says that the login is invalid or
3388              the password is incorrect. The user will not be logged in.
3389              If a user account with the specified login already exists in
3390              BASE, it will be disabled.
3391            </para>
3392          </listitem>
3393         
3394          <listitem>
3395            <para>
3396              The authentication driver says that something else is wrong.
3397              If password caching is enabled, internal authentication will be
3398              used. Otherwise the user will not be logged in. An already
3399              existing account is not modified or disabled.
3400            </para>
3401          </listitem>
3402          </itemizedlist>
3403         
3404          <note>
3405            <para>
3406            The <guilabel>Encrypt password</guilabel> option that is
3407            available on the login page does not work with external
3408            authentication. The simple reason is that the password is
3409            encrypted with a one-way algorithm making it impossible to
3410            call <methodname>Authenticator.authenticate()</methodname>.
3411            </para>
3412          </note>
3413         
3414        </para>
3415      </sect3>
3416     
3417      <sect3 id="plugin_developer.other.authentication.authenticator">
3418        <title>The Authenticator interface</title>
3419       
3420        <para>
3421          To be able to use external authentication you must create a class
3422          that implements the
3423          <interfacename docapi="net.sf.basedb.core.authentication">net.sf.based.core.authentication.Authenticator</interfacename> 
3424          interface. Specify the name of the class in the <property>auth.driver</property> 
3425          setting in <filename>base.config</filename> and
3426          its initialisation parameters in the <property>auth.init</property> setting.
3427        </para>
3428       
3429        <para>
3430          Your class must have a public no-argument constructor. The BASE
3431          application will create only one instance of the class for lifetime
3432          of the BASE server. It must be thread-safe since it may be invoked by
3433          multiple threads at the same time. Here are the methods that you must
3434          implement
3435        </para>
3436       
3437        <variablelist>
3438        <varlistentry>
3439          <term>
3440            <methodsynopsis language="java">
3441              <modifier>public</modifier>
3442              <void/>
3443              <methodname>init</methodname>
3444              <methodparam>
3445                <type>String</type>
3446                <parameter>settings</parameter>
3447              </methodparam>
3448              <exceptionname>AuthenticationException</exceptionname>
3449            </methodsynopsis>
3450          </term>
3451          <listitem>
3452            <para>
3453            This method is called just after the object has been created with its argument
3454            taken from the <property>auth.init</property> setting in your <filename>base.config</filename> 
3455            file. This method is only called once for an instance of the object. The syntax and meaning of
3456            the parameter is driver-dependent and should be documented by the plug-in.
3457            It is irrelevant for the BASE core.
3458            </para>
3459          </listitem>
3460        </varlistentry>
3461       
3462        <varlistentry>
3463          <term>
3464            <methodsynopsis language="java">
3465              <modifier>public</modifier>
3466              <type>boolean</type>
3467              <methodname>supportsExtraInformation</methodname>
3468            </methodsynopsis>
3469          </term>
3470          <listitem>
3471            <para>
3472            This method should simply return <constant>TRUE</constant> or <constant>FALSE</constant> 
3473            depending on if the plug-in supports extra user information or not. The only required
3474            information about a user is a unique ID and the login. Extra information includes
3475            name, address, phone, email, etc.
3476            </para>
3477          </listitem>       
3478        </varlistentry>
3479
3480        <varlistentry>
3481          <term>
3482            <methodsynopsis language="java">
3483              <modifier>public</modifier>
3484              <type>AuthenticationInformation</type>
3485              <methodname>authenticate</methodname>
3486              <methodparam>
3487                <type>String</type>
3488                <parameter>login</parameter>
3489              </methodparam>
3490              <methodparam>
3491                <type>String</type>
3492                <parameter>password</parameter>
3493              </methodparam>
3494              <exceptionname>UnknownLoginException</exceptionname>
3495              <exceptionname>InvalidPasswordException</exceptionname>
3496              <exceptionname>AuthenticationException</exceptionname>
3497            </methodsynopsis>
3498          </term>
3499          <listitem>
3500            <para>
3501            Try to authenticate a login/password combination. The plug-in should return
3502            an <classname docapi="net.sf.basedb.core.authentication">AuthenticationInformation</classname> object if the
3503            authentication is successful or throw an exception if not.
3504           
3505            There are three exceptions to choose from:
3506           
3507            <itemizedlist>
3508            <listitem>
3509              <para>
3510              <exceptionname>UnknownLoginException</exceptionname>:
3511              This exception should be thrown if the login is not known to the
3512              external authentication system.
3513              </para>
3514            </listitem>
3515
3516            <listitem>
3517              <para>
3518              <exceptionname>InvalidPasswordException</exceptionname>:
3519              This exception should be thrown if the login is known but the
3520              password is invalid. In case it is considered a security issue
3521              to reveal that a login exists, the plugin may throw an
3522              <exceptionname>UnknowLoginException</exceptionname> instead.
3523              </para>
3524            </listitem>
3525           
3526            <listitem>
3527              <para>
3528              <exceptionname>AuthenticationException</exceptionname>:
3529              In case there is another problem, such as the authentication service
3530              being down. This exception triggers the use of cached passwords
3531              if caching has been enabled.
3532              </para>
3533            </listitem>
3534            </itemizedlist>
3535            </para>
3536          </listitem>       
3537        </varlistentry>
3538        </variablelist> 
3539      </sect3>
3540     
3541      <sect3 id="plugin_developer.other.authentication.settings">
3542        <title>Configuration settings</title>
3543     
3544        <para>
3545          The configuration settings for the authentication driver are located
3546          in the <filename>base.config</filename> file.
3547          Here is an overview of the settings. For more information read
3548          <xref linkend="appendix.base.config.authentication" />.
3549        </para>
3550       
3551        <variablelist>
3552        <varlistentry>
3553          <term><property>auth.driver</property></term>
3554          <listitem>
3555            <para>
3556            The class name of the authentication plug-in.
3557            </para>
3558          </listitem>
3559        </varlistentry>
3560
3561        <varlistentry>
3562          <term><property>auth.init</property></term>
3563          <listitem>
3564            <para>
3565            Initialisation parameters sent to the plug-in when calling the
3566            <methodname>Authenticator.init()</methodname> method.
3567            </para>
3568          </listitem>
3569        </varlistentry>
3570       
3571        <varlistentry>
3572          <term><property>auth.synchronize</property></term>
3573          <listitem>
3574            <para>
3575            If extra user information is synchronized at login time or not.
3576            This setting is ignored if the driver does not support extra information.
3577            </para>
3578          </listitem>
3579        </varlistentry>
3580       
3581        <varlistentry>
3582          <term><property>auth.cachepasswords</property></term>
3583          <listitem>
3584            <para>
3585              If passwords should be cached by BASE or not. If the passwords are
3586              cached a user may login to BASE even if the external authentication
3587              server is down.
3588            </para>
3589          </listitem>
3590        </varlistentry>
3591       
3592        <varlistentry>
3593          <term><property>auth.daystocache</property></term>
3594          <listitem>
3595            <para>
3596              How many days to cache the passwords if caching has been enabled.
3597              A value of 0 caches the passwords for ever.     
3598            </para>
3599          </listitem>
3600        </varlistentry>
3601        </variablelist>
3602      </sect3>
3603     
3604    </sect2>
3605   
3606    <sect2 id="plugin_developer.other.secondary">
3607      <title>Secondary file storage plugins</title>
3608     
3609      <sect3 id="plugin_developer.other.secondary.vsprimary">
3610        <title>Primary vs. secondary storage</title>
3611        <para>
3612          BASE has support for storing files in two locations, the primary storage and
3613          the secondary storage. The primary storage is always disk-based and must be
3614          accessible by the BASE server as a path on the file system. The path to the
3615          primary storage is configured by the <varname>userfiles</varname> setting in the
3616          <filename>base.config</filename> file. The primary storage is internal to
3617          the core. Client applications don't get access to read or manipulate the
3618          files directly from the file system.
3619        </para>
3620       
3621        <para>
3622          The secondary storage can be anything that can store files. It could, for
3623          example, be another directory, a remote FTP server, or a tape based archiving
3624          system. A file located in the secondary storage is not accessible by the
3625          core, client applications or plug-ins. The secondary storage can only be accessed
3626          by the secondary storage controller. The core (and client) applications uses
3627          flags on the file items to handle the interaction with the secondary storage.
3628        </para>
3629       
3630        <para>
3631          Each file has an <property>action</property> attribute which default's to
3632          <constant>File.Action.NOTHING</constant>. It can take two other values:
3633        </para>
3634       
3635        <orderedlist>
3636        <listitem>
3637          <para>
3638          <constant>File.Action.MOVE_TO_SECONDARY</constant>
3639          </para>
3640        </listitem>
3641        <listitem>
3642          <para>
3643          <constant>File.Action.MOVE_TO_PRIMARY</constant>
3644          </para>
3645        </listitem>
3646        </orderedlist>
3647       
3648        <para>
3649          All files with the action attribute set to <constant>MOVE_TO_SECONDARY</constant> 
3650          should be moved to the secondary storage by the controller, and all files
3651          with the action attribute set to <constant>MOVE_TO_PRIMARY</constant> should be
3652          brought back to primary storage.
3653        </para>
3654       
3655        <para>
3656          The moving of files between primary and secondary storage doesn't happen
3657          immediately. It is up to the server administrator to configure how often and
3658          at what times the controller should check for files that should be moved.
3659          This is configured by the <varname>secondary.storage.interval</varname> 
3660          and <varname>secondary.storage.time</varname> settings in the
3661          <filename>base.config</filename> file.       
3662        </para>
3663      </sect3>
3664     
3665      <sect3 id="plugin_developer.other.secondary.interface">
3666        <title>The SecondaryStorageController interface</title>
3667       
3668        <para>
3669          All you have to do to create a secondary storage controller is to
3670          create a class that implements the
3671          <interfacename docapi="net.sf.basedb.core">net.sf.basedb.core.SecondaryStorageController</interfacename>
3672          interface. In your <filename>base.config</filename> file you then specify the
3673          class name in the <varname>secondary.storage.driver</varname> setting and its
3674          initialisation parameters in the <varname>secondary.storage.init</varname> setting.
3675        </para>
3676
3677        <para>
3678          Your class must have a public no-argument constructor.
3679          The BASE application will create only one instance of the class for
3680          lifetime of the BASE server. Here are the methods that you must implement:
3681        </para>
3682       
3683        <variablelist>
3684        <varlistentry>
3685          <term>
3686          <methodsynopsis language="java">
3687            <modifier>public</modifier>
3688            <void />
3689            <methodname>init</methodname>
3690            <methodparam>
3691              <type>String</type>
3692              <parameter>settings</parameter>
3693            </methodparam>
3694          </methodsynopsis>
3695          </term>
3696          <listitem>
3697            <para>
3698            This method is called just after the object has been created with its argument
3699            taken from the <varname>secondary.storage.init</varname> setting in your
3700            <filename>base.config</filename> file. This method is only called once for
3701            an object.
3702            </para>
3703          </listitem>
3704        </varlistentry>
3705        <varlistentry>
3706          <term>
3707          <methodsynopsis language="java">
3708            <modifier>public</modifier>
3709            <void />
3710            <methodname>run</methodname>
3711          </methodsynopsis>
3712          </term>
3713          <listitem>
3714            <para>
3715            This method is called whenever the core thinks it is time to do some
3716            management of the secondary storage. How often the <methodname>run()</methodname>
3717            method is called is controlled by the <varname>secondary.storage.interval</varname>
3718            and <varname>secondary.storage.time</varname> settings in the
3719            <filename>base.config</filename> file.
3720            When this method is called the controller should:
3721            </para>
3722           
3723            <itemizedlist>
3724            <listitem>
3725              <para>
3726              Move all files which has <constant>action=MOVE_TO_SECONDARY</constant> to
3727              the secondary storage. When the file has been moved call
3728              <methodname>File.setLocation(Location.SECONDARY)</methodname> to tell the
3729              core that the file is now in the secondary storage. You should also call
3730              <methodname>File.setAction(File.Action.NOTHING)</methodname> to reset the
3731              action attribute.
3732              </para>
3733            </listitem>
3734           
3735            <listitem>
3736              <para>
3737              Restore all files which has <constant>action=MOVE_TO_PRIMARY</constant>.
3738              The core will set the location attribute automatically, but you should
3739              call <methodname>File.setAction(File.Action.NOTHING)</methodname> to reset
3740              the action attribute.
3741              </para>
3742            </listitem>
3743           
3744            <listitem>
3745              <para>
3746              Delete all files from the secondary storage that are not present
3747              in the database with <constant>location=Location.SECONDARY</constant>.
3748              This includes files which has been deleted and files that have been
3749              moved offline or re-uploaded.
3750              </para>
3751            </listitem>
3752           
3753            </itemizedlist>
3754           
3755            <para>
3756              As a final act the method should send a message to each user owning
3757              files that has been moved from one location to the other. The message
3758              should include a list of files that has been moved to the secondary
3759              storage and a list of files moved from the secondary storage and a
3760              list of files that has been deleted due to some of the reasons above.
3761            </para>
3762          </listitem>
3763        </varlistentry>       
3764       
3765        <varlistentry>
3766          <term>
3767          <methodsynopsis language="java">
3768            <modifier>public</modifier>
3769            <void />
3770            <methodname>close()</methodname>
3771          </methodsynopsis>
3772          </term>
3773          <listitem>
3774            <para>
3775            This method is called when the server is closing down. After this the object
3776            is never used again.
3777            </para>
3778          </listitem>
3779        </varlistentry>
3780        </variablelist>
3781      </sect3>
3782     
3783      <sect3 id="plugin_developer.other.secondary.settings">
3784        <title>Configuration settings</title>
3785       
3786        <para>
3787        The configuration settings for the secondary storage controller is located in the
3788        <filename>base.config</filename> file. Here is an overview of the settings.
3789        For more information read <xref linkend="appendix.base.config" />.
3790        </para>
3791       
3792        <variablelist>
3793        <varlistentry>
3794          <term><property>secondary.storage.driver</property></term>
3795          <listitem>
3796            <para>
3797            The class name of the secondary storage plug-in.
3798            </para>
3799          </listitem>
3800        </varlistentry>
3801        <varlistentry>
3802          <term><property>secondary.storage.init</property></term>
3803          <listitem>
3804            <para>
3805             Initialisation parameters sent to the plug-in by calling the
3806             <methodname>init()</methodname> method.
3807            </para>
3808          </listitem>
3809        </varlistentry>
3810        <varlistentry>
3811          <term><property>secondary.storage.interval</property></term>
3812          <listitem>
3813            <para>
3814             Interval in seconds between each execution of the secondary storage
3815             controller plug-in.
3816            </para>
3817          </listitem>
3818        </varlistentry>
3819        <varlistentry>
3820          <term><property>secondary.storage.time</property></term>
3821          <listitem>
3822            <para>
3823              Time points during the day when the secondary storage controller plugin
3824              should be executed.
3825            </para>
3826          </listitem>
3827        </varlistentry>
3828        </variablelist>
3829      </sect3>
3830    </sect2>
3831   
3832    <sect2 id="plugin_developer.other.unpacker">
3833      <title>File unpacker plug-ins</title>
3834      <para>
3835        The BASE web client has integrated support for unpacking of
3836        compressed files. See <xref linkend="file_system.handling.upload" />.
3837        Behind the scenes, this support is provided by plug-ins. The standard
3838        BASE distribution comes with support for ZIP files
3839        (<classname docapi="net.sf.basedb.plugins">net.sf.basedb.plugins.ZipFileUnpacker</classname>)
3840        and TAR files (<classname docapi="net.sf.basedb.plugins">net.sf.basedb.plugins.TarFileUnpacker</classname>).
3841      </para>
3842      <para>
3843        To add support for additional compressed formats you have to create a plug-in that
3844        implements the <interfacename docapi="net.sf.basedb.util.zip">net.sf.basedb.util.zip.FileUnpacker</interfacename>
3845        interface. The best way to do this is to extend the
3846        <classname docapi="net.sf.basedb.util.zip">net.sf.basedb.util.zip.AbstractFileUnpacker</classname> which
3847        implements all methods in the <interfacename docapi="net.sf.basedb.core.plugin">Plugin</interfacename>
3848        and <interfacename docapi="net.sf.basedb.core.plugin">InteractivePlugin</interfacename>
3849        interfaces except <methodname>Plugin.getAbout()</methodname>. This leaves
3850        you with the actual unpacking of the files as the only thing to implement.
3851      </para>
3852     
3853      <note>
3854        <title>No support for configurations</title>
3855        The integrated upload in the web interface only works with plug-ins that
3856        does not require a configuration to run.
3857      </note>
3858     
3859      <variablelist>
3860        <title>Methods in the <interfacename docapi="net.sf.basedb.util.zip">FileUnpacker</interfacename> interface</title>
3861        <varlistentry>
3862          <term>
3863            <methodsynopsis language="java">
3864              <modifier>public</modifier>
3865              <type>String</type>
3866              <methodname>getFormatName</methodname>
3867            </methodsynopsis>
3868          </term>
3869          <listitem>
3870            <para>
3871            Return a short string naming the file format. For example:
3872            <constant>ZIP files</constant> or <constant>TAR files</constant>.
3873            </para>
3874          </listitem>
3875        </varlistentry>
3876           
3877        <varlistentry>
3878          <term>
3879            <methodsynopsis language="java">
3880              <modifier>public</modifier>
3881              <type>Set&lt;String&gt;</type>
3882              <methodname>getExtensions</methodname>
3883            </methodsynopsis>
3884          </term>
3885          <listitem>
3886            <para>
3887            Return a set of strings with the file extensions that
3888            are most commonly used with the compressed file format.
3889            For example: <constant>[zip, jar]</constant>. Do not include
3890            the dot in the extensions. The web client and the
3891            <methodname>AbstractFlatFileUnpacker.isInContext()</methodname> method
3892            will use this information to automatically guess which plug-in to
3893            use for unpacking the files.
3894            </para>
3895          </listitem>
3896        </varlistentry>
3897       
3898        <varlistentry>
3899          <term>
3900            <methodsynopsis language="java">
3901              <modifier>public</modifier>
3902              <type>Set&lt;String&gt;</type>
3903              <methodname>getMimeTypes</methodname>
3904            </methodsynopsis>
3905          </term>
3906          <listitem>
3907            <para>
3908            Return a set of string with the MIME types that commonly used with
3909            the compressed file format. For example:
3910            <constant>[application/zip, application/java-archive]</constant>.
3911            This information is used by the
3912            <methodname>AbstractFlatFileUnpacker.isInContext()</methodname> 
3913            method to automatically guess which plug-in to use for unpacking
3914            the files.
3915            </para>
3916          </listitem>
3917        </varlistentry>
3918       
3919        <varlistentry>
3920          <term>
3921            <methodsynopsis language="java">
3922              <modifier>public</modifier>
3923              <type>int</type>
3924              <methodname>unpack</methodname>
3925              <methodparam>
3926                <type>DbControl</type>
3927                <parameter>dc</parameter>
3928              </methodparam>
3929              <methodparam>
3930                <type>Directory</type>
3931                <parameter>dir</parameter>
3932              </methodparam>
3933              <methodparam>
3934                <type>InputStream</type>
3935                <parameter>in</parameter>
3936              </methodparam>
3937              <methodparam>
3938                <type>boolean</type>
3939                <parameter>overwrite</parameter>
3940              </methodparam>
3941              <methodparam>
3942                <type>AbsoluteProgressReporter</type>
3943                <parameter>progress</parameter>
3944              </methodparam>
3945              <exceptionname>IOException</exceptionname>
3946              <exceptionname>BaseException</exceptionname>
3947            </methodsynopsis>
3948          </term>
3949          <listitem>
3950            <para>
3951            Unpack the files and store them in the BASE file system.
3952           
3953            <itemizedlist>
3954            <listitem>
3955              <para>
3956              Do not <methodname>close()</methodname> or
3957              <methodname>commit()</methodname> the
3958              <classname docapi="net.sf.basedb.core">DbControl</classname> passed to this method.
3959              This is done automatically by the
3960              <classname docapi="net.sf.basedb.util.zip">AbstractFileUnpacker</classname> or by the web client.
3961              </para>
3962            </listitem>
3963           
3964            <listitem>
3965              <para>
3966              The <varname>dir</varname> parameter is the root directory where
3967              the unpacked files should be placed. If the compressed file
3968              contains subdirectories the plug-in must create those subdirectories
3969              unless they already exists.
3970              </para>
3971            </listitem>
3972           
3973            <listitem>
3974              <para>
3975              If the <varname>overwrite</varname> parameter is
3976              <constant>FALSE</constant> no existing file should be overwritten
3977              unless the file is <systemitem>OFFLINE</systemitem>.
3978              </para>
3979            </listitem>
3980           
3981            <listitem>
3982              <para>
3983              The <varname>in</varname> parameter is the stream
3984              containing the compressed data. The stream may come
3985              directly from the web upload or from an existing
3986              file in the BASE file system.
3987              </para>
3988            </listitem>
3989           
3990            <listitem>
3991              <para>
3992              The <varname>progress</varname> parameter, if not
3993              <constant>null</constant>, should be used to report the
3994              progress back to the calling code. The plug-in should count
3995              the number of bytes read from the <varname>in</varname>
3996              stream. If it is not possible by other means the stream can
3997              be wrapped by a <classname docapi="net.sf.basedb.util">net.sf.basedb.util.InputStreamTracker</classname>
3998              object which has a <methodname>getNumRead()</methodname> method.
3999              </para>
4000            </listitem>
4001            </itemizedlist>
4002           
4003            </para>
4004          </listitem>
4005        </varlistentry>
4006      </variablelist>
4007     
4008      <para>
4009        When the compressed file is uncompressed during the file upload
4010        from the web interface, the call sequence to the plug-in is slightly
4011        altered from the standard call sequence described in
4012        <xref linkend="plugin_developer.api.callsequence.execute" />.
4013       
4014        <itemizedlist>
4015        <listitem>
4016          <para>
4017          After the plug-in instance has been created, the
4018          <methodname>Plugin.init()</methodname> method is called with <constant>null</constant>
4019          values for both the <varname>configuration</varname> and <varname>job</varname>
4020          parameters.
4021          </para>
4022        </listitem>
4023       
4024        <listitem>
4025          <para>
4026          Then, the <methodname>unpack()</methodname> method is called. The
4027          <methodname>Plugin.run()</methodname> method is never called in this case.
4028          </para>
4029        </listitem>
4030       
4031        </itemizedlist>
4032       
4033      </para>
4034     
4035    </sect2>
4036   
4037    <sect2 id="plugin_developer.other.packer">
4038      <title>File packer plug-ins</title>
4039   
4040      <para>
4041        BASE has support for compressing and downloading a set of selected files and/or
4042        directories. This functionality is provided by a plug-in, the
4043        <classname docapi="net.sf.basedb.plugins">PackedFileExporter</classname>. This plug-in doesn't do the actual
4044        packing itself. This is delegated to classes implementing the
4045        <interfacename docapi="net.sf.basedb.util.zip">net.sf.basedb.util.zip.FilePacker</interfacename> interface.
4046      </para>
4047     
4048      <para>
4049        BASE ships with a number of packing methods, including ZIP and TAR. To
4050        add support for other methods you have to provide an implementation
4051        of the <interfacename docapi="net.sf.basedb.util.zip">FilePacker</interfacename>
4052        interface. Then, create a new configuration for the <classname docapi="net.sf.basedb.plugins">PackedFileExporter</classname>
4053        and enter the name of your class in the configuration wizard.
4054      </para>
4055     
4056      <para>
4057        The <interfacename docapi="net.sf.basedb.util.zip">FilePacker</interfacename> interface is not a regular
4058        plug-in interface (ie. it is not a subinterface to
4059        <interfacename docapi="net.sf.basedb.core.plugin">Plugin</interfacename>). This means that you don't have to
4060        mess with configuration or job parameters. Another difference is that your
4061        class must be installed in Tomcat's classpath (ie. in one of the
4062        <filename>WEB-INF/classes</filename> or <filename>WEB-INF/lib</filename>
4063        folders).
4064      </para>
4065     
4066      <variablelist>
4067        <title>Methods in the <interfacename docapi="net.sf.basedb.util.zip">FilePacker</interfacename> interface</title>
4068        <varlistentry>
4069          <term>
4070            <methodsynopsis language="java">
4071              <modifier>public</modifier>
4072              <type>String</type>
4073              <methodname>getDescription</methodname>
4074            </methodsynopsis>
4075          </term>
4076          <listitem>
4077            <para>
4078            Return a short description the file format that is suitable for use
4079            in dropdown lists in client applications. For example:
4080            <constant>Zip-archive (.zip)</constant> or <constant>TAR-archive (.tar)</constant>.
4081            </para>
4082          </listitem>
4083        </varlistentry>
4084        <varlistentry>
4085          <term>
4086            <methodsynopsis language="java">
4087              <modifier>public</modifier>
4088              <type>String</type>
4089              <methodname>getFileExtension</methodname>
4090            </methodsynopsis>
4091          </term>
4092          <listitem>
4093            <para>
4094            Return the default file extension of the packed format. The returned
4095            value should not include the dot. For example:
4096            <constant>zip</constant> or <constant>tar</constant>.
4097            </para>
4098          </listitem>
4099        </varlistentry>
4100        <varlistentry>
4101          <term>
4102            <methodsynopsis language="java">
4103              <modifier>public</modifier>
4104              <type>String</type>
4105              <methodname>getMimeType</methodname>
4106            </methodsynopsis>
4107          </term>
4108          <listitem>
4109            <para>
4110            Return the standard MIME type of the packed file format.
4111            For example:
4112            <constant>application/zip</constant> or <constant>application/x-tar</constant>.
4113            </para>
4114          </listitem>
4115        </varlistentry>
4116        <varlistentry>
4117          <term>
4118            <methodsynopsis language="java">
4119              <modifier>public</modifier>
4120              <void />
4121              <methodname>setOutputStream</methodname>
4122              <methodparam>
4123                <type>OutputStream</type>
4124                <parameter>out</parameter>
4125              </methodparam>
4126              <exceptionname>IOException</exceptionname>
4127            </methodsynopsis>
4128          </term>
4129          <listitem>
4130            <para>
4131            Sets the outputstream that the packer should write the packed
4132            files to.
4133            </para>
4134          </listitem>
4135        </varlistentry>
4136        <varlistentry>
4137          <term>
4138            <methodsynopsis language="java">
4139              <modifier>public</modifier>
4140              <void />
4141              <methodname>pack</methodname>
4142              <methodparam>
4143                <type>String</type>
4144                <parameter>entryName</parameter>
4145              </methodparam>
4146              <methodparam>
4147                <type>InputStream</type>
4148                <parameter>in</parameter>
4149              </methodparam>
4150              <methodparam>
4151                <type>long</type>
4152                <parameter>size</parameter>
4153              </methodparam>
4154              <methodparam>
4155                <type>long</type>
4156                <parameter>lastModified</parameter>
4157              </methodparam>
4158              <exceptionname>IOException</exceptionname>
4159            </methodsynopsis>
4160          </term>
4161          <listitem>
4162            <para>
4163            Add another file or directory to the packed file. The
4164            <parameter>entryName</parameter> is the name of the new entry, including
4165            path information. The <parameter>in</parameter> is the stream to read
4166            the file data from. If <parameter>in</parameter> is <constant>null</constant>
4167            then the entry denotes a directory. The <parameter>size</parameter> parameter
4168            gives the size in bytes of the file (zero for empty files or directories).
4169            The <parameter>lastModified</parameter>
4170            is that time the file was last modified or 0 if not known.
4171            </para>
4172          </listitem>
4173        </varlistentry>
4174        <varlistentry>
4175          <term>
4176            <methodsynopsis language="java">
4177              <modifier>public</modifier>
4178              <void />
4179              <methodname>close</methodname>
4180              <exceptionname>IOException</exceptionname>
4181            </methodsynopsis>
4182          </term>
4183          <listitem>
4184            <para>
4185            Finish the packing. The packer should release any resources, flush
4186            all data and close all output streams, including the <varname>out</varname> stream
4187            set in the <methodname>setOutputStream</methodname> method.
4188            </para>
4189          </listitem>
4190        </varlistentry>
4191       
4192      </variablelist>
4193     
4194    </sect2>
4195   
4196    <sect2 id="plugin_developer.other.logging">
4197      <title>Logging plug-ins</title>
4198     
4199      <para>
4200        BASE provides a plug-in mechanism for logging changes that are made to items.
4201        This plug-in mechanism is not the same as the regular plug-in API. That is, you do not
4202        have worry about user interaction or implementing the <interfacename 
4203        docapi="net.sf.basedb.core.plugin">Plugin</interfacename> interface.
4204      </para>
4205     
4206      <para>
4207        The logging mechanism works on the data layer level and hooks into
4208        callbacks provided by Hibernate. <interfacename 
4209        docapi="net.sf.basedb.core.log">EntityLogger</interfacename>:s are used to
4210        extract relevant information from Hibernate and create log entries.
4211        While it is possible to have a generic logger it is usually better
4212        to have different implementations depending on the type of entity that
4213        was changed. For example, a change in a child item should, for usability
4214        reasons, be logged as a change in the parent item. Entity loggers
4215        are created by a <interfacename 
4216        docapi="net.sf.basedb.core.log">LogManagerFactory</interfacename>. All changes
4217        made in a single transaction are usually collected by a <interfacename 
4218        docapi="net.sf.basedb.core.log">LogManager</interfacename> which is also
4219        created by the factory.
4220      </para>
4221     
4222      <sect3 id="plugin_developer.other.logmanagerfactory">
4223        <title>The LogManagerFactory interface</title>
4224       
4225        <para>
4226          Which <interfacename 
4227          docapi="net.sf.basedb.core.log">LogManagerFactory</interfacename> to use
4228          is configured in <filename>base.config</filename> (See <xref 
4229          linkend="appendix.base.config.log"/>). A single factory instance is created
4230          when BASE starts and is used for the lifetime of the virtual machine. The
4231          factory implementation must of course be thread-safe. Here is a list of
4232          the methods the factory must implement:
4233        </para>
4234       
4235        <variablelist>
4236          <varlistentry>
4237            <term>
4238              <methodsynopsis language="java">
4239                <modifier>public</modifier>
4240                <type>LogManager</type>
4241                <methodname>getLogManager</methodname>
4242                <methodparam>
4243                  <type>LogControl</type>
4244                  <parameter>logControl</parameter>
4245                </methodparam>
4246              </methodsynopsis>
4247            </term>
4248            <listitem>
4249              <para>
4250              Creates a log manager for a single transaction. Since a transaction
4251              is not thread-safe the log manager implementation doesn't have to
4252              be either. The factory has the possibility to create new log managers
4253              for each transaction.
4254              </para>
4255            </listitem>
4256          </varlistentry>
4257         
4258          <varlistentry>
4259            <term>
4260              <methodsynopsis language="java">
4261                <modifier>public</modifier>
4262                <type>boolean</type>
4263                <methodname>isLoggable</methodname>
4264                <methodparam>
4265                  <type>Object</type>
4266                  <parameter>entity</parameter>
4267                </methodparam>
4268              </methodsynopsis>
4269            </term>
4270            <listitem>
4271              <para>
4272              Checks if changes to the given entity should be
4273              logged or not. For performance reasons, it usually makes sense to
4274              not log everything. For example, the database logger implementation
4275              only logs changes if the entity implements the <interfacename 
4276              docapi="net.sf.basedb.core.data">LoggableData</interfacename>
4277              interface. The return value of this method should be consistent
4278              with <methodname>getEntityLogger()</methodname>.
4279              </para>
4280            </listitem>
4281          </varlistentry>
4282
4283          <varlistentry>
4284            <term>
4285              <methodsynopsis language="java">
4286                <modifier>public</modifier>
4287                <type>EntityLogger</type>
4288                <methodname>getEntityLogger</methodname>
4289                <methodparam>
4290                  <type>LogManager</type>
4291                  <parameter>logManager</parameter>
4292                </methodparam>
4293                <methodparam>
4294                  <type>Object</type>
4295                  <parameter>entity</parameter>
4296                </methodparam>
4297              </methodsynopsis>
4298            </term>
4299            <listitem>
4300              <para>
4301              Create or get an entity logger that knows how to log
4302              changes to the given entity. If the entity should not be
4303              logged, <constant>null</constant> can be returned. This method
4304              is called for each modified item in the transaction.
4305              </para>
4306            </listitem>
4307          </varlistentry>
4308        </variablelist>
4309       
4310      </sect3>
4311     
4312      <sect3 id="plugin_developer.other.logmanager">
4313        <title>The LogManager interface</title>
4314       
4315        <para>
4316          A new log manager is created for each transaction. The log manager
4317          is responsible for collecting all changes made in the transaction
4318          and store those changes in the appropriate place. The interface doesn't define
4319          any methods for this collection, since each implementation may have
4320          very different needs.
4321        </para>
4322       
4323        <variablelist>
4324          <varlistentry>
4325            <term>
4326              <methodsynopsis language="java">
4327                <modifier>public</modifier>
4328                <type>LogControl</type>
4329                <methodname>getLogControl</methodname>
4330              </methodsynopsis>
4331            </term>
4332            <listitem>
4333              <para>
4334              Get the log control object that was supplied by the BASE core
4335              when the transaction was started. The log controller contains
4336              methods for accessing information about the transaction, such
4337              as the logged in user, executing plug-in, etc. It can also
4338              be used to execute queries against the database to get
4339              even more information.
4340              </para>
4341             
4342              <warning>
4343                <para>
4344                  Be careful about the queries that are executed by the log
4345                  controller. Since all logging code is executed at flush
4346                  time in callbacks from Hibernate we are not allowed to
4347                  use the regular session. Instead, all queries are sent
4348                  through the stateless session. The stateless session has no
4349                  caching functionality which means that Hibernate will use
4350                  extra queries to load associations. Our recommendation is
4351                  to avoid quires that return full entities, use scalar
4352                  queries instead to just load the values that are needed.
4353                </para>
4354              </warning>
4355            </listitem>
4356          </varlistentry>
4357          <varlistentry>
4358            <term>
4359              <methodsynopsis language="java">
4360                <modifier>public</modifier>
4361                <void/>
4362                <methodname>afterCommit</methodname>
4363              </methodsynopsis>
4364            </term>
4365            <term>
4366              <methodsynopsis language="java">
4367                <modifier>public</modifier>
4368                <void/>
4369                <methodname>afterRollback</methodname>
4370              </methodsynopsis>
4371            </term>
4372            <listitem>
4373              Called after a successful commit or after a rollback. Note
4374              that the connection to the database has been closed at this
4375              time and it is not possible to save any more information to
4376              it at this time.
4377            </listitem>
4378          </varlistentry>
4379        </variablelist>
4380      </sect3>
4381
4382      <sect3 id="plugin_developer.other.entitylogger">
4383        <title>The EntityLogger interface</title>
4384       
4385        <para>
4386          An entity logger is responsible for extracting the changes
4387          made to an entity and converting it to something that is useful
4388          as a log entry. In most cases, this is not very complicated, but
4389          in some cases, a change in one entity should actually be logged
4390          as a change in a different entity. For example, changes to
4391          annotations are handled by the <classname 
4392          docapi="net.sf.basedb.core.log.db">AnnotationLogger</classname> which
4393          which log it as a change on the parent item.
4394        </para>
4395       
4396        <variablelist>
4397          <varlistentry>
4398            <term>
4399              <methodsynopsis language="java">
4400                <modifier>public</modifier>
4401                <void/>
4402                <methodname>logChanges</methodname>
4403                <methodparam>
4404                  <type>LogManager</type>
4405                  <parameter>logManager</parameter>
4406                </methodparam>
4407                <methodparam>
4408                  <type>EntityDetails</type>
4409                  <parameter>details</parameter>
4410                </methodparam>
4411              </methodsynopsis>
4412            </term>
4413            <listitem>
4414              <para>
4415              This method is called whenever a change has been detected
4416              in an entity. The <varname>details</varname> variable contains
4417              information about the entity and, to a certain degree,
4418              what changes that has been made.
4419              </para>
4420            </listitem>
4421          </varlistentry>
4422        </variablelist>
4423      </sect3>
4424    </sect2>
4425  </sect1>
4426 
4427  <sect1 id="plugin_developer.signals">
4428    <?dbhtml filename="signals.html" ?>
4429    <title>Enable support for aborting a running a plug-in</title>
4430   
4431    <para>
4432      BASE includes a simple signalling system that can be used to send
4433      signals to plug-ins. The system was primarly developed to allow a user
4434      to kill a plug-in when it is executing. Therfore, the focus of this chapter
4435      will be how to implement a plug-in to make it possible to kill it
4436      during it's execution.
4437    </para>
4438   
4439    <para>
4440      Since we don't want to do this by brute force such as destroying the
4441      process or stopping thread the plug-in executes in, cooperation is needed
4442      by the plug-in. First, the plug-in must implement the
4443      <interfacename docapi="net.sf.basedb.core.signal">SignalTarget</interfacename>
4444      interface. From this, a <interfacename 
4445      docapi="net.sf.basedb.core.signal">SignalHandler</interfacename> can be created.
4446      A plug-in may choose to implement it's own signal handler or use an existing
4447      implementation. BASE, for example, provides the <classname docapi="net.sf.basedb.core.signal"
4448      >ThreadSignalHandler</classname> implementation that supports the <constant>ABORT</constant> signal.
4449      This is a simple implementation that just calls <code>Thread.interrupt()</code>
4450      on the plug-in worker thread. This may cause two different effects:
4451    </para>
4452   
4453    <itemizedlist>
4454    <listitem>
4455      <para>
4456      The <code>Thread.interrupted()</code> flag is set. The plug-in must check this
4457      at regular intervals and if the flag is set it must cleanup, rollback
4458      open transactions and exit as soon as possible.
4459      </para>
4460    </listitem>
4461   
4462    <listitem>
4463      <para>
4464      If the plug-in is waiting in a blocking call that is interruptable, for
4465      example <code>Thread.sleep()</code>, an <classname>InterruptedException</classname>
4466      is thrown. This should cause the same actions as if the flag was set to happen.
4467      </para>
4468     
4469      <warning>
4470        <title>Not all blocking calls are interruptable</title>
4471        <para>
4472        For example calling <code>InputStream.read()</code> may leave the
4473        plug-in waiting in a non-interruptable state. In this case there
4474        is nothing BASE can do to wake it up again.
4475        </para>
4476      </warning>
4477    </listitem>
4478    </itemizedlist>
4479   
4480    <para>
4481      Here is a general outline for a plug-in that uses the
4482      <classname>ThreadSignalHandler</classname>.
4483    </para>
4484    <programlisting language="java">
4485<![CDATA[
4486private ThreadSignalHandler signalHandler;
4487public SignalHandler getSignalHandler()
4488{
4489   signalHandler = new ThreadSignalHandler();
4490   return signalHandler;
4491}
4492
4493public void run(Request request, Response response, ProgressReporter progress)
4494{
4495   if (signalHandler != null) signalHandler.setWorkerThread(null);
4496   beginTransaction();
4497   boolean done = false;
4498   boolean interrupted = false;
4499   while (!done && !interrupted)
4500   {
4501      try
4502      {
4503         done = doSomeWork(); // NOTE! This must not take forever!
4504         interrupted = Thread.interrupted();
4505      }
4506      catch (InterruptedException ex)
4507      {
4508         // NOTE! Try-catch is only needed if thread calls
4509         // a blocking method that is interruptable
4510         interrupted = true;
4511      }
4512   }
4513   if (interrupted)
4514   {
4515      rollbackTransaction();
4516      response.setError("Aborted by user", null);
4517   }
4518   else
4519   {
4520      commitTransaction();
4521      response.setDone("Done");
4522   }
4523}
4524]]>
4525</programlisting>
4526   
4527    <para>
4528      Other signal handler implementations are
4529      <classname docapi="net.sf.basedb.core.signal">ProgressReporterSignalHandler</classname>
4530      and <classname docapi="net.sf.basedb.core.signal">EnhancedThreadSignalHandler</classname>.
4531      The latter handler also has support for the <constant>SHUTDOWN</constant> signal
4532      which is sent to plug-in when the system is shutting down. Clever plug-ins may use
4533      this to enable them to be restarted when the system is up and running again.
4534      See that javadoc for information about how to use it. For more information about the
4535      signalling system as a whole, see <xref linkend="core_api.signals" />.
4536    </para>
4537   
4538  </sect1>
4539 
4540  <sect1 id="plugin_developer.classload">
4541    <?dbhtml filename="classloading.html" ?>
4542    <title>How BASE load plug-in classes</title>
4543   
4544    <para>
4545      All plug-ins should be installed in the location specified by the
4546      <varname>plugins.dir</varname> setting in <filename>base.config</filename>.
4547      While it is possible to also install them in a location that is on the
4548      classpath, for example <filename>&lt;base-dir&gt;/www/WEB-INF/lib</filename>,
4549      it is nothing that we recommend. The rest of the information in this section
4550      only applies to plug-ins that have been installed in the <varname>plugins.dir</varname>
4551      location.
4552    </para>
4553   
4554    <para>
4555      If the above recommendation has been followed BASE will use it's
4556      own classloader to load the plug-in classes. This have several
4557      benefits:
4558    </para>
4559   
4560    <itemizedlist>
4561    <listitem>
4562      <para>
4563      New plug-ins can be installed and existing plug-ins can be updated
4564      without restarting the web server. If the <property>plugins.autounload</property>
4565      setting in <filename>base.config</filename> has been enabled all you have to
4566      do to update a plug-in is to replace the JAR file with a new version.
4567      BASE will automatically load the new classes the next time the plug-in
4568      is used. If the option isn't enabled, the server admin has to manually
4569      unload the old code from the web interface first.
4570      </para>
4571    </listitem>
4572   
4573    <listitem>
4574      <para>
4575      Plug-ins may use it's own 3-rd party libraries without interfering
4576      with other plug-ins. This may be important because a plug-in may depend on
4577      a certain version of a library while another plug-in may depend on
4578      a different version. Since BASE is using different class-loaders
4579      for different plug-ins this is not a problem.
4580      </para>
4581    </listitem>
4582     
4583    </itemizedlist>
4584   
4585    <para>
4586      The classloading scheme used by BASE also means plug-in
4587      developers must pay attention to a few things:
4588    </para>
4589   
4590    <itemizedlist>
4591    <listitem>
4592      <para>
4593      A plug-in can only access/use classes from it's own JAR file, BASE core
4594      classes, Java system classes and from JAR files listed in the plug-in's
4595      <filename>MANIFEST.MF</filename> file. See <xref linkend="plugin_developer.organize" />.
4596      </para>
4597    </listitem>
4598   
4599    <listitem>
4600      <para>
4601      A plug-in can also access other plug-ins, but only via the methods and
4602      interfaces defined in BASE. In the following example we assume that
4603      there are two plug-ins, <classname>ex.MyPlugin</classname> 
4604      and <classname>ex.MyOtherPlugin</classname>,
4605      located in two different JAR files. The code below is executing
4606      in the <classname>ex.MyPlugin</classname>:
4607      </para>
4608     
4609      <programlisting language="java">
4610// Prepare to load MyOtherPlugin
4611SessionControl sc = ...
4612DbControl dc = ...
4613PluginDefinition def = PluginDefinition.getByClassName(dc, "ex.MyOtherPlugin");
4614
4615// Ok
4616Plugin other = def.newInstance();
4617
4618// Not ok; fails with ClassCastException
4619MyOtherPlugin other = (MyOtherPlugin)def.newInstance();
4620
4621// Ok; since now we are using the correct class loader
4622MyOtherPlugin other = def.newInstance(MyOtherPlugin.class);
4623</programlisting>
4624     
4625      <para>
4626      The first call succeeds because it uses the <interfacename 
4627      docapi="net.sf.basedb.core.plugin">Plugin</interfacename>
4628      interface which is defined by BASE. This class is loaded by the web servers
4629      class loader and is the same for all plug-ins.
4630      </para>
4631     
4632      <para>
4633      The second call fails because BASE uses a different classloader to load the
4634      <classname>ex.MyOtherPlugin</classname> class. This
4635      class is not (in Java terms) the same as the <classname>ex.MyOtherPlugin</classname> 
4636      class loaded by the classloader that loaded the <classname>ex.MyPlugin</classname>
4637      class. If, on the other hand, both plug-ins are located in the same
4638      JAR file BASE uses the same classloader and the second call will succeed.
4639      </para>
4640     
4641      <para>
4642      The third call succeeds because now that we specify the class as an argument,
4643      BASE uses that classloader instead.
4644      </para>
4645     
4646      <para>
4647      Another option is that the <classname>ex.MyPlugin</classname>
4648      lists the JAR file where <classname>ex.MyOtherPlugin</classname> is
4649      located in it's <filename>MANIFEST.MF</filename> file. Then, the following
4650      code can be used: <code>MyOtherPlugin other = new MyOtherPlugin();</code>
4651      </para>
4652     
4653    </listitem>
4654    </itemizedlist>
4655   
4656    <para>
4657      Tomcat includes a good document describing how classloading is implemented
4658      in Tomcat: <ulink url="http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html"
4659        >http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html</ulink>.
4660     
4661      BASE's classloading scheme isn't as complex as Tomcat's, but it
4662      very similar to how Tomcat loads different web applications.
4663      The figure on the linked document could be extended with another level
4664      with separate classloaders for each plug-in as child
4665      classloaders to the web application classloaders. 
4666    </para>
4667   
4668    <para>
4669      As of BASE 2.13 the default search order for classes has been changed. The
4670      default is now to first look in the plug-ins class path (eg. in the same
4671      JAR file and in files listed in the <filename>MANIFEST.MF</filename> file).
4672      Only if the class is not found the search is delegated to the parent class
4673      loader. This behaviour can be changed by setting <code>X-Delegate-First: true</code>
4674      in the <filename>MANIFEST.MF</filename> file. If this property is set the
4675      parent class loader is search first. This is the same as in BASE 2.12 and
4676      earlier.
4677    </para>
4678   
4679    <note>
4680      The benefit with the new search order is that plug-ins may use
4681      a specific version of any external package even if the same
4682      package is part of the BASE distribution. This was not possible before
4683      since the package in the BASE distribution was loaded first.
4684    </note>
4685  </sect1>
4686 
4687  <sect1 id="plugin_developer.example">
4688    <?dbhtml filename="examples.html" ?>
4689    <title>Example plug-ins (with download)</title>
4690    <para>
4691      We have created some example plug-ins which demonstrates how to
4692      use the plug-in system and how to create an interactive plug-in that
4693      can ask a user for one or more parameters.
4694   
4695      You can download
4696      a tar file with the source and compiled plug-in code from the BASE plug-ins website:
4697      <ulink url="http://baseplugins.thep.lu.se/wiki/net.sf.basedb.examples.extensions"
4698      >http://baseplugins.thep.lu.se/wiki/net.sf.basedb.examples.extensions</ulink>
4699    </para>
4700   
4701  </sect1>
4702</chapter>
Note: See TracBrowser for help on using the repository browser.