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

Last change on this file since 3759 was 3759, checked in by Martin Svensson, 14 years ago

References #364, documentation

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