source: branches/webservices/doc/src/docbook/developerdoc/plugin_developer.xml @ 3653

Last change on this file since 3653 was 3653, checked in by Jari Häkkinen, 16 years ago

Merged log:trunk#3581:3649 into the wwebservices branch.

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