source: trunk/doc/src/docbook/developerdoc/plugin_developer.xml @ 3763

Last change on this file since 3763 was 3763, checked in by Martin Svensson, 16 years ago

References #364, Added instructions how to make ant put base-plugins.xml and base-configurations.xml
in the plugin jar-file when building an external plugin.
Also updated the examples about this.

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