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

Last change on this file since 3838 was 3838, checked in by Nicklas Nordborg, 15 years ago

Fixes #805: Add syntax coloring to code examples in docbook

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