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

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

References #364, documentation

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