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

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

References #108: Logging the change history of an item

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