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

Last change on this file since 3872 was 3872, checked in by Nicklas Nordborg, 14 years ago

References #807: Doc is now read for reading

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