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

Last change on this file since 5407 was 5407, checked in by Nicklas Nordborg, 13 years ago

References #1509, #1503 and #1502. Updated documentation related to the mentioned tickets.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 175.7 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 5407 2010-09-13 10:53:20Z 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 on of the
664                <methodname>Response.setDone()</methodname>,
665                <methodname>Response.setError()</methodname> or
666                <methodname>Response.setContinue()</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, the <methodname>Response.setError()</methodname>
1756          method to report errors or the <methodname>Response.setContinue()</methodname>
1757          method to respond to a shutdown signal and tell the core to resume the
1758          job once the system is up and running again.
1759          </para>
1760        </listitem>
1761       
1762        <listitem>
1763          <para>
1764          In all cases the <methodname>Plugin.done()</methodname>
1765          method is called and the plug-in instance is discarded.
1766          </para>
1767        </listitem>
1768       
1769        </orderedlist>
1770      </sect3>
1771   
1772    </sect2>
1773 
1774    <sect2 id="plugin_developer.api.jspparameters">
1775      <title>Using custom JSP pages for parameter input</title>
1776
1777        <para>
1778          This is an advanced option for plug-ins that require a different interface for
1779          specifying plug-in parameters than the default list showing one parameter at a
1780          time. This feature is used by setting the
1781          <methodname>RequestInformation.getJspPage()</methodname>
1782          property when constructing the request information object. If this property has
1783          a non-null value, the web client will send the browser to the specified JSP page
1784          instead of to the generic parameter input page.
1785        </para>
1786        <para>
1787          When setting the JSP page you can either specify an absolute path
1788          of only the filename of the JSP file. If only the filename is specified,
1789          the JSP file is expected to be located in a special location, generated
1790          from the package name of your plug-in. If the plug-in is located in the package
1791          <classname>org.company</classname> the JSP file must be located in
1792          <filename class="directory">&lt;base-dir&gt;/www/plugins/org/company/</filename>.
1793        </para>
1794       
1795        <para>
1796          An absolute path starts with '/' and may or may not include the
1797          root directory of the BASE installation. If, for example, BASE is intalled to
1798          <userinput>http://your.base.server.com/base</userinput>, the following absolute
1799          paths are equivalent  <filename>/base/path/to/file.jsp</filename>,
1800          <filename>/path/to/file.jsp</filename>.
1801        </para>
1802        <para>
1803          In both cases, please note that the browser still thinks that it is showing
1804          the regular parameter input page at the usual location:
1805          <filename class="directory">&lt;base-dir&gt;/www/common/plugin/index.jsp</filename>.
1806          All links in your JSP page should be relative to that directory.
1807        </para>
1808        <para>
1809          Even if you use your own JSP page we recommend that you use the built-in
1810          facility for passing the parameters back to the plug-in. For this to work you
1811          must:
1812        </para>
1813        <itemizedlist spacing="compact">
1814          <listitem>
1815            <simpara>
1816            Generate the list of <classname docapi="net.sf.basedb.core">PluginParameter</classname> 
1817            objects as usual.
1818            </simpara>
1819          </listitem>
1820          <listitem>
1821            <simpara>
1822              Name all your input fields in the JSP like:
1823              <parameter>
1824                parameter:<replaceable>name-of-parameter</replaceable>
1825              </parameter>
1826            </simpara>
1827<programlisting language="java:nogutter">
1828// Plug-in generate PluginParameter
1829StringParameterType stringPT = new StringParameterType(255, null, true);
1830PluginParameter one = new PluginParameter("one", "One", "First string", stringPT);
1831PluginParameter two = new PluginParameter("two", "Two", "Second string", stringPT);
1832
1833// JSP should name fields as:
1834First string: &lt;input type="text" name="parameter:one"&gt;&lt;br&gt;
1835Second string: &lt;input type="text" name="parameter:two"&gt;
1836</programlisting>
1837          </listitem>
1838          <listitem>
1839          <simpara>
1840            Send the form to
1841            <filename>index.jsp</filename>
1842            with the <varname>ID</varname>,
1843            <varname>cmd</varname> and <varname>requestId</varname> 
1844            parameters as shown below.
1845          </simpara>
1846<programlisting language="xml">
1847&lt;form action="index.jsp" method="post"&gt;
1848&lt;input type="hidden" name="ID" value="&lt;%=ID%&gt;"&gt;
1849&lt;input type="hidden" name="requestId" value="&lt;%=request.getParameter("requestId")%&gt;"&gt;
1850&lt;input type="hidden" name="cmd" value="SetParameters"&gt;
1851...
1852&lt;/form&gt;
1853</programlisting>
1854          <simpara>
1855            The <varname>ID</varname> is the session ID for the logged
1856            in user and is required. The <varname>requestId</varname>
1857            is the ID for this particular plug-in/job configuration
1858            sequence. It is optional, but we recommend that you use it
1859            since it protects your plug-in from getting mixed up with
1860            other plug-in configuration wizards. The <varname>cmd</varname>
1861            tells BASE to send the parameters to the plug-in for validation
1862            and saving.
1863          </simpara>
1864
1865          <simpara>
1866            Values are sent as strings to BASE that converts them to the
1867            proper value type before they are passed on to your plug-in.
1868            However, there is one case that can't be
1869            accurately represented with custom JSP pages, namely 'null' values.
1870            A null value is sent by not sending any value at all. This is not
1871            possible with a fixed form. It is of course possible to add some custom
1872            JavaScript that adds and removes form elements as needed, but it is
1873            also possible to let the empty string represent null. Just include a
1874            hidden parameter like this if you want an empty value for the 'one'
1875            parameter converted to null:
1876          </simpara>
1877<programlisting language="xml">
1878&lt;input type="hidden" name="parameter:one:emptyIsNull" value="1"&gt;
1879</programlisting>
1880          </listitem>
1881          </itemizedlist>
1882         
1883          <para>
1884            If you want a &gbCancel; button to abort the configuration
1885            you should reload the page with with the url:
1886            <uri>index.jsp?ID=&lt;%=ID%&gt;&amp;cmd=CancelWizard</uri>. This
1887            allows BASE to clean up resources that has been put in global
1888            session variables.
1889          </para>
1890         
1891            <para>
1892              In your JSP page you will probably need to access some information like the
1893              <classname docapi="net.sf.basedb.core">SessionControl</classname>, <classname docapi="net.sf.basedb.core">Job</classname>
1894              and possible even the <classname docapi="net.sf.basedb.core">RequestInformation</classname>
1895              object created by your plug-in.
1896            </para>
1897<programlisting language="java">
1898// Get session control and its ID (required to post to index.jsp)
1899final SessionControl sc = Base.getExistingSessionControl(pageContext, true);
1900final String ID = sc.getId();
1901
1902// Get information about the current request to the plug-in
1903PluginConfigurationRequest pcRequest =
1904   (PluginConfigurationRequest)sc.getSessionSetting("plugin.configure.request");
1905PluginDefinition plugin =
1906   (PluginDefinition)sc.getSessionSetting("plugin.configure.plugin");
1907PluginConfiguration pluginConfig =
1908   (PluginConfiguration)sc.getSessionSetting("plugin.configure.config");
1909PluginDefinition job =
1910   (PluginDefinition)sc.getSessionSetting("plugin.configure.job");
1911RequestInformation ri = pcRequest.getRequestInformation();
1912</programlisting>
1913
1914    </sect2>
1915  </sect1>
1916
1917  <sect1 id="plugin_developer.import">
1918    <title>Import plug-ins</title>
1919
1920    <para>
1921      A plugin becomes an import plugin simply by returning
1922      <constant>Plugin.MainType.IMPORT</constant>
1923      from the <methodname>Plugin.getMainType()</methodname> method.
1924    </para>
1925   
1926    <sect2 id="plugin_developer.import.autodetect">
1927      <title>Autodetect file formats</title>
1928      <para>
1929        BASE has built-in functionality for autodetecting file formats.
1930        Your plug-in can be part of that feature if it reads it data
1931        from a single file. It must also implement the
1932        <interfacename docapi="net.sf.basedb.core.plugin">AutoDetectingImporter</interfacename>
1933        interface.
1934      </para>
1935
1936      <sect3 id="plugin_developer.api.interfaces.autodetecting">
1937        <title>The net.sf.basedb.core.plugin.AutoDetectingImporter interface</title>
1938
1939        <variablelist>
1940        <varlistentry>
1941          <term>
1942            <methodsynopsis language="java">
1943              <modifier>public</modifier>
1944              <type>boolean</type>
1945              <methodname>isImportable</methodname>
1946              <methodparam>
1947                <type>InputStream</type>
1948                <parameter>in</parameter>
1949              </methodparam>
1950              <exceptionname>BaseException</exceptionname>
1951            </methodsynopsis>
1952          </term>
1953          <listitem>
1954            <para>
1955              Check the input stream if it seems to contain data that can be imported by
1956              the plugin. Usually it means scanning a few lines for some header
1957              mathing a predefined string or a regexp.
1958            </para>
1959            <para>
1960              The <classname docapi="net.sf.basedb.plugins">AbstractFlatFileImporter</classname> implements this method
1961              by reading the headers from the input stream and checking if
1962              it stopped at an unknown type of line or not:
1963                <programlisting language="java">
1964public final boolean isImportable(InputStream in)
1965   throws BaseException
1966{
1967   FlatFileParser ffp = getInitializedFlatFileParser();
1968   ffp.setInputStream(in);
1969   try
1970   {
1971      ffp.nextSection();
1972      FlatFileParser.LineType result = ffp.parseHeaders();
1973      if (result == FlatFileParser.LineType.UNKNOWN)
1974      {
1975         return false;
1976      }
1977      else
1978      {
1979         return isImportable(ffp);
1980      }
1981   }
1982   catch (IOException ex)
1983   {
1984      throw new BaseException(ex);
1985   }
1986}
1987</programlisting>
1988            </para>
1989            <para>
1990              Note that the input stream doesn't have to be a text file.
1991              It can be any type of file, for example a binary or an XML file.
1992              In the case of an XML file you would need to validate the entire
1993              input stream in order to be a 100% sure that it is a valid
1994              xml file, but we recommend that you only check the first few XML tags,
1995              for example, the &lt;!DOCTYPE &gt; declaration and/or the root element
1996              tag.
1997            </para>
1998          </listitem>
1999        </varlistentry>
2000        <varlistentry>
2001          <term>
2002            <methodsynopsis language="java">
2003              <modifier>public</modifier>
2004              <void/>
2005              <methodname>doImport</methodname>
2006              <methodparam>
2007                <type>InputStream</type>
2008                <parameter>in</parameter>
2009              </methodparam>
2010              <methodparam>
2011                <type>ProgressReporter</type>
2012                <parameter>progress</parameter>
2013              </methodparam>
2014              <exceptionname>BaseException</exceptionname>
2015            </methodsynopsis>
2016          </term>
2017          <listitem>
2018            <para>
2019              Parse the input stream and import all data that is found.
2020              This method is of course only called if the
2021              <methodname>isImportable()</methodname> has returned true. Note
2022              however that the input stream is reopened at the start of the
2023              file. It may even be the case that the <methodname>isImportable()</methodname>
2024              method is called on one instance of the plugin and the
2025              <methodname>doImport()</methodname> method is called on another.
2026              Thus, the <methodname>doImport()</methodname> can't rely on any state set
2027              by the <methodname>isImportable()</methodname> method.
2028            </para>
2029          </listitem>
2030        </varlistentry>
2031        </variablelist>
2032       
2033        <tip>
2034          <title>Try casting to ImportInputStream</title>
2035          <para>
2036            As of BASE 2.9 the auto-detect functionality uses a
2037            <classname docapi="net.sf.basedb.core.plugin">ImportInputStream</classname>
2038            as the <varname>in</varname> parameter. This class contains some
2039            metadata about the file the input stream is originating from. The most
2040            useful feature is the possibility to get information about the
2041            character set used in the file. This makes it possible to open text
2042            files using the correct character set.
2043          </para>
2044          <programlisting language="java">
2045String charset = Config.getCharset(); // Default value
2046if (in instanceof ImportInputStream)
2047{
2048   ImportInputStream iim = (ImportInputStream)in;
2049   if (iim.getCharacterSet() != null) charset = iim.getCharacterSet();
2050}
2051Reader reader = new InputStreamReader(in, Charset.forName(charset)));
2052</programlisting>
2053        </tip>       
2054      </sect3>
2055     
2056      <sect3 id="plugin_developer.import.autodetect.callsequence">
2057        <title>Call sequence during autodetection</title>
2058       
2059        <para>
2060          The call sequence for autodetection resembles the call sequence for
2061          checking if the plug-in can be used in a given context.
2062        </para>
2063
2064        <orderedlist>
2065        <listitem>
2066          <para>
2067          A new instance of the plug-in class is created. The plug-in must
2068          have a public no-argument constructor.
2069          </para>
2070        </listitem>
2071       
2072        <listitem>
2073          <para>
2074          The <methodname>Plugin.init()</methodname> method is called.
2075          The <varname>job</varname> parameter is <constant>null</constant>.
2076          The <varname>configuration</varname> parameter is <constant>null</constant>
2077          if the plug-in does not have any configuration parameters.
2078          </para>
2079        </listitem>
2080       
2081        <listitem>
2082          <para>
2083          If the plug-in is interactive the the <methodname>InteractivePlugin.isInContext()</methodname>
2084          is called. If the context is a list context, the <varname>item</varname>
2085          parameter is null, otherwise the current item is passed. The plug-in
2086          should return <constant>null</constant> if it can be used under the
2087          current circumstances, or a message explaining why not.
2088          </para>
2089        </listitem>
2090       
2091        <listitem>
2092          <para>
2093          If the plug-in can be used the <methodname>AutoDetectingImporter.isImportable()</methodname>
2094          method is called to check if the selected file is importable or not.
2095          </para>
2096        </listitem>
2097       
2098        <listitem>
2099          <para>
2100          After this, <methodname>Plugin.done()</methodname> is called and
2101          the plug-in instance is discarded. If there are
2102          several configurations for a plug-in, this procedure is repeated
2103          for each configuration. If the plug-in can be used without
2104          a configuration the procedure is also repeated without
2105          configuration parameters.
2106          </para>
2107        </listitem>
2108       
2109        <listitem>
2110          <para>
2111          If a single plug-in was found the user is taken to the regular
2112          job configuration wizard. A new plug-in instance is created for
2113          this. If more than one plug-in was found the user is presented
2114          with a list of the plug-ins. After selecting one of them the
2115          regular job configuration wizard is used with a new plug-in instance.
2116          </para>
2117        </listitem>
2118       
2119        </orderedlist>
2120       
2121      </sect3>
2122
2123    </sect2>
2124   
2125    <sect2 id="plugin_developer.import.abstractflatfileimporter">
2126      <title>The AbstractFlatFileImporter superclass</title>
2127      <para>
2128        The <classname docapi="net.sf.basedb.plugins">AbstractFlatFileImporter</classname> is a very useful abstract
2129        class to use as a superclass for your own import plug-ins. It can be used
2130        if your plug-in uses regular text files that can be parsed by an instance of the
2131        <classname docapi="net.sf.basedb.util">net.sf.basedb.util.FlatFileParser</classname> class. This class parses a file
2132        by checking each line against a few regular expressions. Depending on which regular
2133        expression matches the line, it is classified as a header line, a section line,
2134        a comment, a data line, a footer line or unknown. Header lines are inspected as a group,
2135        but data lines individually, meaning that it consumes very little memory since only
2136        a few lines at a time needs to be loaded.
2137      </para>
2138     
2139      <para>
2140        The <classname docapi="net.sf.basedb.plugins">AbstractFlatFileImporter</classname> defines
2141        <classname docapi="net.sf.basedb.core">PluginParameter</classname> objects
2142        for each of the regular expressions and other parameters used by the parser. It also
2143        implements the <methodname>Plugin.run()</methodname> method and does most of
2144        the ground work for instantiating a <methodname>FlatFileParser</methodname> and
2145        parsing the file. What you have to do in your plugin is to put together the
2146        <classname docapi="net.sf.basedb.core">RequestInformation</classname> objects
2147        for configuring the plugin and creating a job and implement the
2148        <methodname>InteractivePlugin.configure()</methodname> method for validating and
2149        storing the parameters. You should also implement or override some methods
2150        defined by <classname>AbstractFlatFileImporter</classname>.
2151      </para>
2152
2153      <para>
2154      Here is what you need to do:
2155      </para>
2156
2157      <itemizedlist>
2158      <listitem>
2159        <para>
2160        Implement the <methodname>Plugin.getAbout()</methodname> method. See
2161        <xref linkend="plugin_developer.api.interfaces.plugin" /> for more information.
2162        </para>
2163      </listitem>
2164     
2165      <listitem>
2166        <para>
2167        Implement the <interfacename docapi="net.sf.basedb.core.plugin">InteractivePlugin</interfacename> methods.
2168        See <xref linkend="plugin_developer.api.interfaces.interactive" /> for more information. Note that the
2169        <classname docapi="net.sf.basedb.plugins">AbstractFlatFileImporter</classname>
2170        has defined many parameters for regular expressions used by the parser
2171        already. You should just pick them and put in your <classname docapi="net.sf.basedb.core">RequestInformation</classname>
2172        object.
2173        </para>
2174       
2175        <programlisting language="java">
2176// Parameter that maps the items name from a column
2177private PluginParameter&lt;String&gt; nameColumnMapping;
2178
2179// Parameter that maps the items description from a column
2180private PluginParameter&lt;String&gt; descriptionColumnMapping;
2181
2182private RequestInformation getConfigurePluginParameters(GuiContext context)
2183{
2184   if (configurePlugin == null)
2185   {
2186      // To store parameters for CONFIGURE_PLUGIN
2187      List&lt;PluginParameter&lt;?&gt;&gt; parameters =
2188         new ArrayList&lt;PluginParameter&lt;?&gt;&gt;();
2189
2190      // Parser regular expressions - from AbstractFlatFileParser
2191      parameters.add(parserSection);
2192      parameters.add(headerRegexpParameter);
2193      parameters.add(dataHeaderRegexpParameter);
2194      parameters.add(dataSplitterRegexpParameter);
2195      parameters.add(ignoreRegexpParameter);
2196      parameters.add(dataFooterRegexpParameter);
2197      parameters.add(minDataColumnsParameter);
2198      parameters.add(maxDataColumnsParameter);
2199
2200      // Column mappings
2201      nameColumnMapping = new PluginParameter&lt;String&gt;(
2202         "nameColumnMapping",
2203         "Name",
2204         "Mapping that picks the items name from the data columns",
2205         new StringParameterType(255, null, true)
2206      );
2207   
2208      descriptionColumnMapping = new PluginParameter&lt;String&gt;(
2209        "descriptionColumnMapping",
2210        "Description",
2211        "Mapping that picks the items description from the data columns",
2212        new StringParameterType(255, null, false)
2213      );
2214
2215      parameters.add(mappingSection);
2216      parameters.add(nameColumnMapping);
2217      parameters.add(descriptionColumnMapping);
2218     
2219      configurePlugin = new RequestInformation
2220      (
2221         Request.COMMAND_CONFIGURE_PLUGIN,
2222         "File parser settings",
2223         "",
2224         parameters
2225      );
2226
2227   }
2228   return configurePlugin;
2229}
2230</programlisting>
2231      </listitem>
2232     
2233      <listitem>
2234        <para>
2235        Implement/override some of the methods defined by
2236        <classname>AbstractFlatFileParser</classname>. The most important
2237        methods are listed below.
2238        </para>
2239      </listitem>
2240     
2241      </itemizedlist>
2242
2243      <variablelist>
2244      <varlistentry>
2245        <term>
2246          <methodsynopsis language="java">
2247            <modifier>protected</modifier>
2248            <type>FlatFileParser</type>
2249            <methodname>getInitializedFlatFileParser</methodname>
2250            <exceptionname>BaseException</exceptionname>
2251          </methodsynopsis>
2252        </term>
2253        <listitem>
2254          <para>
2255          The method is called to create a <classname docapi="net.sf.basedb.util.parser">FlatFileParser</classname>
2256          and set the regular expressions that should be used for parsing the file.
2257          The default implementation assumes that your plug-in has used the built-in
2258          <classname docapi="net.sf.basedb.core">PluginParameter</classname> objects and has stored the values
2259          at the configuration level. You should override this method if you need to
2260          initialise the parser in a different way. See for example the
2261          code for the <classname docapi="net.sf.basedb.plugins">PrintMapFlatFileImporter</classname> plug-in which
2262          has a fixed format and doesn't use configurations.
2263          </para>
2264          <programlisting language="java">
2265@Override
2266protected FlatFileParser getInitializedFlatFileParser()
2267   throws BaseException
2268{
2269   FlatFileParser ffp = new FlatFileParser();
2270   ffp.setSectionRegexp(Pattern.compile("\\[(.+)\\]"));
2271   ffp.setHeaderRegexp(Pattern.compile("(.+)=,(.*)"));
2272   ffp.setDataSplitterRegexp(Pattern.compile(","));
2273   ffp.setDataFooterRegexp(Pattern.compile(""));
2274   ffp.setMinDataColumns(12);
2275   return ffp;
2276}
2277</programlisting>
2278        </listitem>
2279      </varlistentry>
2280     
2281      <varlistentry>
2282        <term>
2283          <methodsynopsis language="java">
2284            <modifier>protected</modifier>
2285            <type>boolean</type>
2286            <methodname>isImportable</methodname>
2287            <methodparam>
2288              <type>FlatFileParser</type>
2289              <parameter>ffp</parameter>
2290            </methodparam>
2291            <exceptionname>IOException</exceptionname>
2292          </methodsynopsis>
2293        </term>
2294        <listitem>
2295          <para>
2296          This method is called from the <methodname>isImportable(InputStream)</methodname>
2297          method, AFTER <methodname>FlatFileParser.nextSection()</methodname> and
2298          <methodname>FlatFileParser.parseHeaders()</methodname> has been called
2299          a single time and if the <methodname>parseHeaders</methodname> method didn't
2300          stop on an unknown line. The default implementation of this method always returns
2301          TRUE, since obviously some data has been found. A subclass may override this method
2302          if it wants to do more checks, for example, make that a certain header is present
2303          with a certain value. It may also continue parsing the file. Here is a code example from
2304          the <classname docapi="net.sf.basedb.plugins">PrintMapFlatFileImporter</classname> which checks if a
2305          <constant>FormatName</constant> header is present and contains either
2306          <constant>TAM</constant> or <constant>MwBr</constant>.
2307          </para>
2308         
2309          <programlisting language="java">
2310/**
2311   Check that the file is a TAM or MwBr file.
2312   @return TRUE if a FormatName header is present and contains "TAM" or "MwBr", FALSE
2313      otherwise
2314*/
2315@Override
2316protected boolean isImportable(FlatFileParser ffp)
2317{
2318   String formatName = ffp.getHeader("FormatName");
2319   return formatName != null &amp;&amp; 
2320      (formatName.contains("TAM") || formatName.contains("MwBr"));
2321}
2322</programlisting>
2323        </listitem>
2324      </varlistentry>
2325     
2326      <varlistentry>
2327        <term>
2328          <methodsynopsis language="java">
2329            <modifier>protected</modifier>
2330            <void/>
2331            <methodname>begin</methodname>
2332            <methodparam>
2333              <type>FlatFileParser</type>
2334              <parameter>ffp</parameter>
2335            </methodparam>
2336            <exceptionname>BaseException</exceptionname>
2337          </methodsynopsis>
2338        </term>
2339        <listitem>
2340          <para>
2341          This method is called just before the parsing of the file
2342          begins. Override this method if you need to initialise some
2343          internal state. This is, for example, a good place to open
2344          a <classname docapi="net.sf.basedb.core">DbControl</classname> object, read parameters from the
2345          job and configuration and put them into more useful variables. The default
2346          implementation does nothing, but we recommend that
2347          <methodname>super.begin()</methodname> is always called.
2348          </para>
2349          <programlisting language="java">
2350// Snippets from the RawDataFlatFileImporter class
2351private DbControl dc;
2352private RawDataBatcher batcher;
2353private RawBioAssay rawBioAssay;
2354private Map&lt;String, String&gt; columnMappings;
2355private int numInserted;
2356
2357@Override
2358protected void begin()
2359   throws BaseException
2360{
2361   super.begin();
2362
2363   // Get DbControl
2364   dc = sc.newDbControl();
2365   rawBioAssay = (RawBioAssay)job.getValue(rawBioAssayParameter.getName());
2366
2367   // Reload raw bioassay using current DbControl
2368   rawBioAssay = RawBioAssay.getById(dc, rawBioAssay.getId());
2369   
2370   // Create a batcher for inserting spots
2371   batcher = rawBioAssay.getRawDataBatcher();
2372
2373   // For progress reporting
2374   numInserted = 0;
2375}         
2376</programlisting>
2377        </listitem>
2378      </varlistentry>
2379      <varlistentry>
2380        <term>
2381          <methodsynopsis language="java">
2382            <modifier>protected</modifier>
2383            <void/>
2384            <methodname>handleHeader</methodname>
2385            <methodparam>
2386              <type>FlatFileParser.Line</type>
2387              <parameter>line</parameter>
2388            </methodparam>
2389            <exceptionname>BaseException</exceptionname>
2390          </methodsynopsis>
2391        </term>
2392        <listitem>
2393          <para>
2394          This method is called once for every header line that is found in
2395          the file. The <varname>line</varname> parameter contains information
2396          about the header. The default implementation of this method does
2397          nothing.
2398          </para>
2399          <programlisting language="java">
2400@Override
2401protected void handleHeader(Line line)
2402   throws BaseException
2403{
2404   super.handleHeader(line);
2405   if (line.name() != null &amp;&amp; line.value() != null)
2406   {
2407      rawBioAssay.setHeader(line.name(), line.value());
2408   }
2409}
2410</programlisting>
2411        </listitem>
2412      </varlistentry>
2413      <varlistentry>
2414        <term>
2415          <methodsynopsis language="java">
2416            <modifier>protected</modifier>
2417            <void/>
2418            <methodname>handleSection</methodname>
2419            <methodparam>
2420              <type>FlatFileParser.Line</type>
2421              <parameter>line</parameter>
2422            </methodparam>
2423            <exceptionname>BaseException</exceptionname>
2424          </methodsynopsis>
2425        </term>
2426        <listitem>
2427          <para>
2428            This method is called once for each section that is found in the file.
2429            The <varname>line</varname> parameter contains information
2430            about the section. The default implementation of this method does
2431            nothing.
2432          </para>
2433        </listitem>
2434      </varlistentry>
2435     
2436      <varlistentry>
2437        <term>
2438          <methodsynopsis language="java">
2439            <modifier>protected abstract</modifier>
2440            <void/>
2441            <methodname>beginData</methodname>
2442            <exceptionname>BaseException</exceptionname>
2443          </methodsynopsis>
2444        </term>
2445        <listitem>
2446          <para>
2447          This method is called after the headers has been parsed, but before
2448          the first line of data. This is a good place to add code that
2449          depends on information in the headers, for example, put
2450          together column mappings.
2451          </para>
2452         
2453          <programlisting language="java">
2454private Mapper reporterMapper;
2455private Mapper blockMapper;
2456private Mapper columnMapper;
2457private Mapper rowMapper;
2458// ... more mappers
2459
2460@Override
2461protected void beginData()
2462{
2463   boolean cropStrings = ("crop".equals(job.getValue("stringTooLongError")));
2464
2465   // Mapper that always return null; used if no mapping expression has been entered
2466   Mapper nullMapper = new ConstantMapper((String)null);
2467   
2468   // Column mappers
2469   reporterMapper = getMapper(ffp, (String)configuration.getValue("reporterIdColumnMapping"),
2470      cropStrings ? ReporterData.MAX_EXTERNAL_ID_LENGTH : null, nullMapper);
2471   blockMapper = getMapper(ffp, (String)configuration.getValue("blockColumnMapping"),
2472      null, nullMapper);
2473   columnMapper = getMapper(ffp, (String)configuration.getValue("columnColumnMapping"),
2474      null, nullMapper);
2475   rowMapper = getMapper(ffp, (String)configuration.getValue("rowColumnMapping"),
2476      null, nullMapper);
2477   // ... more mappers: metaGrid coordinate, X-Y coordinate, extended properties
2478   // ...
2479}
2480</programlisting>
2481         
2482        </listitem>
2483      </varlistentry>
2484       
2485      <varlistentry>
2486        <term>
2487          <methodsynopsis language="java">
2488            <modifier>protected abstract</modifier>
2489            <void/>
2490            <methodname>handleData</methodname>
2491            <methodparam>
2492              <type>FlatFileParser.Data</type>
2493              <parameter>data</parameter>
2494            </methodparam>
2495            <exceptionname>BaseException</exceptionname>
2496          </methodsynopsis>
2497        </term>
2498        <listitem>
2499          <para>
2500          This method is abstract and must be implemented by all subclasses.
2501          It is called once for every data line in the the file.
2502          </para>
2503
2504          <programlisting language="java">
2505// Snippets from the RawDataFlatFileImporter class
2506@Override
2507protected void handleData(Data data)
2508   throws BaseException
2509{
2510   // Create new RawData object
2511   RawData raw = batcher.newRawData();
2512
2513   // External ID for the reporter
2514   String externalId = reporterMapper.getValue(data);
2515   
2516   // Block, row and column numbers
2517   raw.setBlock(blockMapper.getInt(data));
2518   raw.setColumn(columnMapper.getInt(data));
2519   raw.setRow(rowMapper.getInt(data));
2520   // ... more: metaGrid coordinate, X-Y coordinate, extended properties
2521   
2522   // Insert raw data to the database
2523   batcher.insert(raw, externalId);
2524   numInserted++;
2525}
2526</programlisting> 
2527         
2528        </listitem>
2529      </varlistentry>
2530     
2531      <varlistentry>
2532        <term>
2533          <methodsynopsis language="java">
2534            <modifier>protected</modifier>
2535            <void/>
2536            <methodname>end</methodname>
2537            <methodparam>
2538              <type>boolean</type>
2539              <parameter>success</parameter>
2540            </methodparam>
2541          </methodsynopsis>
2542        </term>
2543        <listitem>
2544          <para>
2545            Called when the parsing has ended, either because the end of
2546            file was reached or because an error has occurred. The subclass
2547            should close any open resources, ie. the <classname docapi="net.sf.basedb.core">DbControl</classname>
2548            object. The <varname>success</varname> parameter is <constant>true</constant>
2549            if the parsing was successful, <constant>false</constant> otherwise.
2550            The default implementation does nothing.
2551          </para>
2552         
2553          <programlisting language="java">
2554@Override
2555protected void end(boolean success)
2556   throws BaseException
2557{
2558   try
2559   {
2560      // Commit if the parsing was successful
2561      if (success)
2562      {
2563         batcher.close();
2564         dc.commit();
2565      }
2566   }
2567   catch (BaseException ex)
2568   {
2569      // Well, now we got an exception
2570      success = false;
2571      throw ex;
2572   }
2573   finally
2574   {
2575      // Always close... and call super.end()
2576      if (dc != null) dc.close();
2577      super.end(success);
2578   }
2579}     
2580</programlisting>         
2581        </listitem>
2582      </varlistentry>
2583     
2584      <varlistentry>
2585        <term>
2586          <methodsynopsis language="java">
2587            <modifier>protected</modifier>
2588            <type>String</type>
2589            <methodname>getSuccessMessage</methodname>
2590            <void/>
2591          </methodsynopsis>
2592        </term>
2593        <listitem>
2594          <para>
2595          This is the last method that is called, and it is only called if
2596          everything went suceessfully. This method allows a subclass to generate
2597          a short message that is sent back to the database as a final progress
2598          report. The default implementation returns null, which means that no
2599          message will be generated.
2600          </para>
2601          <programlisting language="java">
2602@Override
2603protected String getSuccessMessage()
2604{
2605   return numInserted + " spots inserted";
2606}
2607</programlisting>
2608        </listitem>
2609      </varlistentry>
2610      </variablelist>
2611
2612      <para>
2613        The <classname docapi="net.sf.basedb.plugins">AbstractFlatFileImporter</classname> has a lot of
2614        other methods that you may use and/or override in your own plug-in.
2615        Check the javadoc for more information.
2616      </para>
2617
2618    </sect2>
2619  </sect1>
2620
2621  <sect1 id="plugin_developer.export">
2622    <title>Export plug-ins</title>
2623   
2624    <para>
2625      Export plug-ins are plug-ins that takes data from BASE, and
2626      prepares it for use with some external entity. Usually this
2627      means that data is taken from the database and put into a file
2628      with some well-defined file format.
2629      An export plug-in should return <constant>MainType.EXPORT</constant> from
2630      the <methodname>Plugin.getMainType()</methodname> method.
2631    </para>
2632   
2633    <sect2 id="plugin_developer.export.download">
2634      <title>Immediate download of exported data</title>
2635      <para>
2636        An export plug-in may want to give the user a choice
2637        between saving the exported data in the BASE file system
2638        or to download it immediately to the client computer. With the basic
2639        plug-in API the second option is not possible. The
2640        <interfacename docapi="net.sf.basedb.core.plugin">ImmediateDownloadExporter</interfacename> is an
2641        interface that extends the <interfacename docapi="net.sf.basedb.core.plugin">Plugin</interfacename>
2642        interface to provide this functionality. If your export
2643        plug-in wants to provide immediate download functionality it must
2644        implement the <interfacename docapi="net.sf.basedb.core.plugin">ImmediateDownloadExporter</interfacename>
2645        interface.
2646      </para>
2647     
2648      <sect3 id="plugin_developer.export.immediatedownloadexporter">
2649      <title>The ImmediateDownloadExporter interface</title>
2650      <variablelist>
2651      <varlistentry>
2652        <term>
2653          <methodsynopsis language="java">
2654            <modifier>public</modifier>
2655            <void/>
2656            <methodname>doExport</methodname>
2657            <methodparam>
2658              <type>ExportOutputStream</type>
2659              <parameter>out</parameter>
2660            </methodparam>
2661            <methodparam>
2662              <type>ProgressReporter</type>
2663              <parameter>progress</parameter>
2664            </methodparam>
2665          </methodsynopsis>
2666        </term>
2667        <listitem>
2668          <para>
2669          Perform the export. The plug-in should write the
2670          exported data to the <varname>out</varname> stream.
2671          If the <varname>progress</varname> parameter is not null,
2672          the progress should be reported at regular interval in the
2673          same manner as in the <methodname>Plugin.run()</methodname>
2674          method.
2675          </para>
2676        </listitem>
2677      </varlistentry>
2678      </variablelist>
2679     
2680      </sect3>
2681     
2682      <sect3 id="plugin_developer.export.exportoutputstream">
2683      <title>The ExportOutputStream class</title>
2684     
2685      <para>
2686        The <classname docapi="net.sf.basedb.core.plugin">ExportOutputStream</classname> is
2687        an extension to the
2688        <classname>java.io.OutputStream</classname>. Use the regular
2689        <methodname>write()</methodname> methods to write data to it.
2690        It also has some additional methods, which are used for setting
2691        metadata about the generated file. These methods are useful, for
2692        example, when generating HTTP response headers.
2693      </para>
2694     
2695      <note>
2696        <para>
2697        These methods must be called before starting to write data to
2698        the <varname>out</varname> stream.
2699        </para>
2700      </note>
2701     
2702      <variablelist>
2703      <varlistentry>
2704        <term>
2705          <methodsynopsis language="java">
2706            <modifier>public</modifier>
2707            <void/>
2708            <methodname>setContentLength</methodname>
2709            <methodparam>
2710              <type>long</type>
2711              <parameter>contentLength</parameter>
2712            </methodparam>
2713          </methodsynopsis>
2714        </term>
2715        <listitem>
2716          <para>
2717          Set the total size of the exported data. Don't call this method if the
2718          total size is not known.
2719          </para>
2720        </listitem>
2721      </varlistentry>
2722      <varlistentry>
2723        <term>
2724          <methodsynopsis language="java">
2725            <modifier>public</modifier>
2726            <void/>
2727            <methodname>setMimeType</methodname>
2728            <methodparam>
2729              <type>String</type>
2730              <parameter>mimeType</parameter>
2731            </methodparam>
2732          </methodsynopsis>
2733        </term>
2734        <listitem>
2735          <para>
2736          Set the MIME type of the file that is being generated.
2737          </para>
2738        </listitem>
2739      </varlistentry>
2740      <varlistentry>
2741        <term>
2742          <methodsynopsis language="java">
2743            <modifier>public</modifier>
2744            <void/>
2745            <methodname>setCharacterSet</methodname>
2746            <methodparam>
2747              <type>String</type>
2748              <parameter>charset</parameter>
2749            </methodparam>
2750          </methodsynopsis>
2751        </term>
2752        <listitem>
2753          <para>
2754          Sets the character set used in text files. For example,
2755          UTF-8 or ISO-8859-1.
2756          </para>
2757        </listitem>
2758      </varlistentry>
2759      <varlistentry>
2760        <term>
2761          <methodsynopsis language="java">
2762            <modifier>public</modifier>
2763            <void/>
2764            <methodname>setFilename</methodname>
2765            <methodparam>
2766              <type>String</type>
2767              <parameter>filename</parameter>
2768            </methodparam>
2769          </methodsynopsis>
2770        </term>
2771        <listitem>
2772          <para>
2773          Set a suggested name of the file that is being
2774          generated.
2775          </para>
2776        </listitem>
2777      </varlistentry>
2778      </variablelist>
2779     
2780      </sect3>
2781     
2782      <sect3 id="plugin_developer.export.callsequence">
2783        <title>Call sequence during immediate download</title>
2784
2785      <para>
2786        Supporting immediate download also means that the method call
2787        sequence is a bit altered from the standard sequence described
2788        in <xref linkend="plugin_developer.api.callsequence.execute" />.
2789      </para>
2790     
2791      <itemizedlist>
2792      <listitem>
2793        <para>
2794        The plug-in must call <methodname>Response.setDownloadImmediately()</methodname>
2795        instead of <methodname>Response.setDone()</methodname> in <methodname>Plugin.configure()</methodname>
2796        to end the job configuration wizard. This requests that the core starts
2797        an immediate download.
2798        </para>
2799       
2800        <note>
2801          <para>
2802          Even if an immediate download is requested by the plug-in this feature
2803          may have been disabled by the server administrator. If so, the plug-in
2804          can choose if the job should be added to job queue or if this is an
2805          error condition.
2806          </para>
2807        </note>
2808      </listitem>
2809     
2810      <listitem>
2811        <para>
2812        If immediate download is granted the web client will keep the
2813        same plug-in instance and call <methodname>ImmediateDownloadExporter.doExport()</methodname>.
2814        In this case, the <methodname>Plugin.run()</methodname> is never called.
2815        After the export, <methodname>Plugin.done()</methodname> is called as
2816        usual.
2817        </para>
2818       
2819      </listitem>
2820
2821      <listitem>
2822        <para>
2823        If immediate download is not granted and the job is added to the job queue
2824        the regular job execution sequence is used.
2825        </para>
2826      </listitem>
2827      </itemizedlist>
2828     
2829      </sect3>
2830
2831    </sect2>
2832   
2833    <sect2 id="plugin_developer.export.abstractexporter">
2834      <title>The AbstractExporterPlugin class</title>
2835   
2836      <para>
2837        This is an abstract superclass that will make it easier
2838        to implement export plug-ins that support immediate
2839        download. It defines <classname docapi="net.sf.basedb.core">PluginParameter</classname>
2840        objects for asking a user about a path where the exported
2841        data should be saved and if existing files should be overwritten or not.
2842        If the user leaves the path empty the immediate download functionality
2843        should be used. It also contains implementations of both the
2844        <methodname>Plugin.run()</methodname> method and the
2845        <methodname>ImmediateDownloadExporter.doExport()</methodname> method.
2846        Here is what you need to do in your own plug-in code (code examples are
2847        taken from the <classname docapi="net.sf.basedb.plugins">HelpExporter</classname>):
2848      </para>
2849     
2850      <itemizedlist>
2851      <listitem>
2852        <para>
2853          Your plug-in should extend the <classname docapi="net.sf.basedb.core.plugin">AbstractExporterPlugin</classname>
2854          class:
2855          <programlisting language="java">
2856public class HelpExporter
2857  extends AbstractExporterPlugin
2858  implements InteractivePlugin
2859</programlisting>
2860        </para>
2861      </listitem>
2862     
2863      <listitem>
2864        <para>
2865          You need to implement the
2866          <methodname>InteractivePlugin.getRequestInformation()</methodname>
2867          method. Use the <methodname>getSaveAsParameter()</methodname>
2868          and <methodname>getOverwriteParameter()</methodname> methods defined in the
2869          superclass to create plug-in parameters that asks for the file name to save
2870          to and if existing files can be overwritten or not.
2871          You should also check if the administrator has enabled the immediate execution
2872          functionality for your plug-in. If not, the only option is to
2873          export to a file in the BASE file system and the filename is a
2874          required parameter.
2875         
2876          <programlisting language="java">
2877// Selected parts of the getRequestConfiguration() method
2878...
2879List&lt;PluginParameter&lt;?&gt;&gt; parameters =
2880   new ArrayList&lt;PluginParameter&lt;?&gt;&gt;();
2881...
2882PluginDefinition pd = job.getPluginDefinition();
2883boolean requireFile = pd == null ?
2884   false : !pd.getAllowImmediateExecution();
2885
2886parameters.add(getSaveAsParameter(null, null, defaultPath, requireFile));
2887parameters.add(getOverwriteParameter(null, null));
2888
2889configureJob = new RequestInformation
2890(
2891   Request.COMMAND_CONFIGURE_JOB,
2892   "Help exporter options",
2893   "Set Client that owns the helptexts, " +
2894     "the file path where the export file should be saved",
2895   parameters
2896);
2897....
2898return configureJob;
2899</programlisting>
2900        </para>
2901      </listitem>
2902
2903      <listitem>
2904        <para>
2905        You must also implement the <methodname>configure()</methodname>
2906        method and check the parameters. If no filename has been given,
2907        you should check if immediate exection is allowed and set an
2908        error if it is not. If a filename is present, use the
2909        <methodname>pathCanBeUsed()</methodname> method to check if
2910        it is possible to save the data to a file with that name. If the
2911        file already exists it can be overwritten if the <varname>OVERWRITE</varname>
2912        is <constant>TRUE</constant> or if the file has been flagged for removal.
2913        Do not forget to store the parameters with the <methodname>storeValue()</methodname>
2914        method.
2915
2916        <programlisting language="java">
2917// Selected parts from the configure() method
2918if (request.getParameterValue(SAVE_AS) == null)
2919{
2920   if (!request.isAllowedImmediateExecution())
2921   {
2922      response.setError("Immediate download is not allowed. " +
2923         "Please specify a filename.", null);
2924      return;
2925   }
2926   Client client = (Client)request.getParameterValue("client");
2927   response.setDownloadImmediately("Export help texts for client application " +
2928     client.getName(), ExecutionTime.SHORTEST, true);
2929}
2930else
2931{
2932   if (!pathCanBeUsed((String)request.getParameterValue(SAVE_AS),
2933      (Boolean)request.getParameterValue(OVERWRITE)))
2934   {
2935      response.setError("File exists: " +
2936         (String)request.getParameterValue(SAVE_AS), null);
2937      return;
2938   }
2939   storeValue(job, request, ri.getParameter(SAVE_AS));
2940   storeValue(job, request, ri.getParameter(OVERWRITE));
2941   response.setDone("The job configuration is complete", ExecutionTime.SHORTEST);
2942}
2943</programlisting>
2944       
2945        </para>
2946      </listitem>
2947     
2948      <listitem>
2949        <para>
2950        Implement the <methodname>performExport()</methodname> method.
2951        This is defined as abstract in the <classname docapi="net.sf.basedb.core.plugin">AbstractExporterPlugin</classname>
2952        class. It has the same parameters as the <methodname>ImmediateDownloadExporter.doExport()</methodname>
2953        method and they have the same meaning. The only difference is that the
2954        <varname>out</varname> stream can be linked to a file in the BASE filesystem
2955        and not just to the HTTP response stream.
2956        </para>
2957      </listitem>
2958     
2959      <listitem>
2960        <para>
2961        Optionally, implement the <methodname>begin()</methodname>,
2962        <methodname>end()</methodname> and <methodname>getSuccessMessage()</methodname>
2963        methods. Theese methods do nothing by default.
2964        </para>
2965      </listitem>
2966      </itemizedlist>
2967     
2968      <para>
2969        The call sequence for plug-ins extending <classname docapi="net.sf.basedb.core.plugin">AbstractExporterPlugin</classname>
2970        is:
2971      </para>
2972     
2973      <orderedlist>
2974      <listitem>
2975        <para>
2976        Call <methodname>begin()</methodname>.
2977        </para>
2978      </listitem>
2979      <listitem>
2980        <para>
2981        Call <methodname>performExport()</methodname>.
2982        </para>
2983      </listitem>
2984      <listitem>
2985        <para>
2986        Call <methodname>end()</methodname>.
2987        </para>
2988      </listitem>
2989      <listitem>
2990        <para>
2991        Call <methodname>getSuccessMessage()</methodname> if running as a regular
2992        job. This method is never called when doing an immediate download since there
2993        is no place to show the message.
2994        </para>
2995      </listitem>
2996      </orderedlist>
2997     
2998    </sect2>
2999   
3000  </sect1>
3001 
3002  <sect1 id="plugin_developer.analyse">
3003    <title>Analysis plug-ins</title>
3004    <para>
3005      A plug-in becomes an analysis plug-in simply by returning
3006      <constant>Plugin.MainType.ANALYZE</constant> from the
3007      <methodname>Plugin.getMainType()</methodname> method. The information returned from
3008      <methodname>InteractivePlugin.getGuiContexts()</methodname>
3009      must include: [<constant>Item.BIOASSAYSET</constant>, <constant>Type.ITEM</constant>]
3010      since this is the main place where the web client looks for analysis plug-ins. If
3011      the plug-in can work on a subset of the bioassays it may also include
3012      [<constant>Item.BIOASSAY</constant>, <constant>Type.LIST</constant>]
3013      among the contexts. This will make it possible for a user to select
3014      bioassays from the list and then invoke the plug-in.
3015    </para>
3016   
3017<programlisting language="java">
3018private static final Set&lt;GuiContext&gt; guiContexts =
3019   Collections.singleton(new GuiContext(Item.BIOASSAYSET, GuiContext.Type.ITEM));
3020
3021public Set&lt;GuiContext&gt; getGuiContexts()
3022{
3023   return guiContexts;
3024}</programlisting>
3025
3026    <para>
3027    If the plugin depends on a specific raw data type or on the number of
3028    channels, it should check that the current bioassayset is of the
3029    correct type in the <methodname>InteractivePlugin.isInContext()</methodname> 
3030    method. It is also a good idea to check if the current user has permission
3031    to use the current experiment. This permission is needed to create new bioassaysets or
3032    other data belonging to the experiment.
3033    </para>
3034 
3035    <programlisting language="java">
3036public boolean isInContext(GuiContext context, Object item)
3037{
3038   if (item == null)
3039   {
3040      message = "The object is null";
3041   }
3042   else if (!(item instanceof BioAssaySet))
3043   {
3044      message = "The object is not a BioAssaySet: " + item;
3045   }
3046   else
3047   {
3048      BioAssaySet bas = (BioAssaySet)item;
3049      int channels = bas.getRawDataType().getChannels();
3050      if (channels != 2)
3051      {
3052         message = "This plug-in requires 2-channel data, not " + channels + "-channel.";
3053      }
3054      else
3055      {
3056         Experiment e = bas.getExperiment();
3057         e.checkPermission(Permission.USE);
3058      }
3059   }
3060}
3061</programlisting>
3062
3063    <para>
3064    The plugin should always include a parameter asking for the current
3065    bioassay set when the <methodname>InteractivePlugin.getRequestInformation()</methodname>
3066    is called with <literal>command = Request.COMMAND_CONFIGURE_JOB</literal>.
3067    </para> 
3068
3069    <programlisting language="java">
3070private static final RequestInformation configurePlugin;
3071private RequestInformation configureJob;
3072private PluginParameter&lt;BioAssaySet&gt; bioAssaySetParameter;
3073
3074public RequestInformation getRequestInformation(GuiContext context, String command)
3075   throws BaseException
3076{
3077   RequestInformation requestInformation = null;
3078   if (command.equals(Request.COMMAND_CONFIGURE_PLUGIN))
3079   {
3080      requestInformation = getConfigurePlugin(context);
3081   }
3082   else if (command.equals(Request.COMMAND_CONFIGURE_JOB))
3083   {
3084      requestInformation = getConfigureJob(context);
3085   }
3086   return requestInformation;
3087}
3088
3089private RequestInformation getConfigureJob(GuiContext context)
3090{
3091   if (configureJob == null)
3092   {
3093      bioAssaySetParameter; = new PluginParameter&lt;BioAssaySet&gt;(
3094         "bioAssaySet",
3095         "Bioassay set",
3096         "The bioassay set used as the source for this analysis plugin",
3097         new ItemParameterType&lt;BioAssaySet&gt;(BioAssaySet.class, null, true, 1, null)
3098      );
3099
3100      List&lt;PluginParameter&lt;?&gt;&gt; parameters = new ArrayList&lt;PluginParameter&lt;?&gt;&gt;();
3101      parameters.add(bioAssaySetParameter);
3102      // Add more plug-in-specific parameters here...
3103   
3104      configureJob = new RequestInformation(
3105         Request.COMMAND_CONFIGURE_JOB,
3106         "Configure job",
3107         "Set parameter for plug-in execution",
3108         parameters
3109      );
3110   }
3111   return configureJob;
3112}
3113</programlisting>
3114
3115    <para>
3116    Of course, the <methodname>InteractivePlugin.configure()</methodname> method needs
3117    to validate and store the bioassay set parameter as well:
3118    </para>
3119 
3120    <programlisting language="java">
3121public void configure(GuiContext context, Request request, Response response)
3122{
3123   String command = request.getCommand();
3124   try
3125   {
3126      if (command.equals(Request.COMMAND_CONFIGURE_PLUGIN))
3127      {
3128         // Validate and store configuration parameters
3129         response.setDone("Plugin configuration complete");
3130      }
3131      else if (command.equals(Request.COMMAND_CONFIGURE_JOB))
3132      {
3133         List&lt;Throwable&gt; errors =
3134            validateRequestParameters(configureJob.getParameters(), request);
3135         if (errors != null)
3136         {
3137            response.setError(errors.size() +
3138               " invalid parameter(s) were found in the request", errors);
3139            return;
3140         }
3141         storeValue(job, request, bioAssaySetParameter);
3142         // Store other plugin-specific parameters
3143     
3144         response.setDone("Job configuration complete", Job.ExecutionTime.SHORT);
3145      }
3146   }
3147   catch (Throwable ex)
3148   {
3149      // Never throw exception, always set response!
3150      response.setError(ex.getMessage(), Arrays.asList(ex));
3151   }
3152}
3153</programlisting>
3154
3155    <para>
3156    Now, the typical <methodname>Plugin.run()</methodname> method loads the specfied bioassay set
3157    and its spot data. It may do some filtering and recalculation of the spot
3158    intensity value(s). In most cases it will store the result as a child bioassay
3159    set with one bioassay for each bioassay in the parent bioassay set.
3160    Here is an example, which just copies the intensity values, while
3161    removing those with a negative value in either channel.
3162    </para>
3163 
3164    <programlisting language="java">
3165public void run(Request request, Response response, ProgressReporter progress)
3166{
3167   DbControl dc = sc.newDbControl();
3168   try
3169   {
3170      BioAssaySet source = (BioAssaySet)job.getParameter("bioAssaySet");
3171      // Reload with current DbControl
3172      source = BioAssaySet.getById(dc, source.getId());
3173      int channels = source.getRawDataType().getChannels();
3174     
3175      // Create transformation and new bioassay set
3176      Job j = Job.getById(dc, job.getId());
3177      Transformation t = source.newTransformation(j);
3178      t.setName("Copy spot intensities &gt;= 0");
3179      dc.saveItem(t);
3180
3181      BioAssaySet result = t.newProduct(null, "new", true);
3182      result.setName("After: Copying spot intensities");
3183      dc.saveItem(result);
3184
3185      // Get query for source data
3186      DynamicSpotQuery query = source.getSpotData();
3187     
3188      // Do not return spots with intensities &lt; 0
3189      for (int ch = 1; ch &lt;= channels; ++ch)
3190      {
3191         query.restrict(
3192            Restrictions.gteq(
3193               Dynamic.column(VirtualColumn.channel(ch)),
3194               Expressions.integer(0)
3195            )
3196         );
3197      }
3198     
3199      // Create batcher and copy data
3200      SpotBatcher batcher = result.getSpotBatcher();
3201      int spotsCopied = batcher.insert(query);
3202      batcher.close();
3203     
3204      // Commit and return
3205      dc.commit();
3206      response.setDone("Copied " + spotsCopied + " spots.");
3207   }
3208   catch (Throwable t)
3209   {
3210      response.setError(t.getMessage(), Arrays.asList(t));
3211   }
3212   finally
3213   {
3214      if (dc != null) dc.close();
3215   }
3216}
3217</programlisting>
3218
3219    <para>
3220    See <xref linkend="api_overview.dynamic_and_batch_api" />
3221    for more examples of using the analysis API.
3222    </para>
3223
3224    <sect2 id="plugin_developer.analyse.abstractanalysis">
3225      <title>The AbstractAnalysisPlugin class</title>
3226     
3227      <para>
3228        This class is an abstract base class. It is a useful
3229        class for most analysis plug-ins to inherit from. Its main
3230        purpose is to define <classname docapi="net.sf.basedb.core">PluginParameter</classname>
3231        objects that are commonly used in analysis plug-ins. This includes:
3232      </para>
3233     
3234      <itemizedlist>
3235      <listitem>
3236        <para>
3237        The source bioassay set:
3238          <methodname>getSourceBioAssaySetParameter()</methodname>,
3239          <methodname>getCurrentBioAssaySet()</methodname>,
3240          <methodname>getSourceBioAssaySet()</methodname>
3241        </para>
3242      </listitem>
3243      <listitem>
3244        <para>
3245        The optional restriction of which bioassays to use.
3246        All bioassays in a bioassay set will be used if this
3247        parameter is empty. This is useful when the plugin only
3248        should run on a subset of bioassays in a bioassay set:
3249          <methodname>getSourceBioAssaysParameter()</methodname>,
3250          <methodname>getSourceBioAssays()</methodname>
3251        </para>
3252      </listitem>
3253      <listitem>
3254        <para>
3255        The name and description of the child bioassay set that
3256        is going to be created by the plug-in:
3257        <methodname>getChildNameParameter()</methodname>,
3258        <methodname>getChildDescriptionParameter()</methodname>
3259        </para>
3260      </listitem>
3261      <listitem>
3262        <para>
3263        The name and description of the transformation that
3264        represents the execution of the plug-in:
3265        <methodname>getTransformationNameParameter()</methodname>,
3266        <methodname>getTransformationName()</methodname>
3267        </para>
3268      </listitem>
3269      </itemizedlist>
3270     
3271    </sect2>
3272   
3273    <sect2 id="plugin_developer.analyse.filterplugin">
3274      <title>The AnalysisFilterPlugin interface</title>
3275     
3276      <para>
3277        The <interfacename docapi="net.sf.basedb.core.plugin">net.sf.basedb.core.plugin.AnalysisFilterPlugin</interfacename> 
3278        is a tagging interface, with no methods, that all analysis plug-ins that only filters
3279        data should implement. The benefit is that they will be linked from the
3280        <guibutton>Filter bioassay set</guibutton> button and not just
3281        the <guibutton>Run analysis</guibutton> button. They will also get
3282        a different icon in the experiment outline to make filtering
3283        transformations appear different from other transformations.
3284      </para>
3285     
3286      <para>
3287        The interface exists purely for making the user interaction better. There is
3288        no harm in not implementing it since the plug-in will always appear in
3289        from the <guibutton>Run analysis</guibutton> button. On the other hand,
3290        it doesn't cost anything to implement the interface since it doesn't
3291        have any methods.
3292      </para>
3293   
3294    </sect2>
3295
3296  </sect1>
3297 
3298  <sect1 id="plugin_developer.other">
3299    <title>Other plug-ins</title>
3300    <para></para>
3301   
3302    <sect2 id="plugin_developer.other.authentication">
3303      <title>Authentication plug-ins</title>
3304     
3305      <para>
3306        BASE provides a plug-in mechanism for authenticating users
3307        (validating the username and password) when they are logging in.
3308        This plug-in mechanism is not the same as the regular plug-in API.
3309        That is, you do not have worry about user interaction or implementing the
3310        <interfacename docapi="net.sf.basedb.core.plugin">Plugin</interfacename> interface.
3311      </para>
3312     
3313      <sect3 id="plugin_developer.other.authentication.internal_external">
3314        <title>Internal vs. external authentation</title>
3315     
3316        <para>
3317          BASE can authenticate users in two ways. Either it uses the internal
3318          authentication or the external authentication. With internal
3319          authentication BASE stores logins and passwords in its own database.
3320          With external authentication this is handled by some external
3321          application. Even with external authentication it is possible to
3322          let BASE cache the logins/passwords. This makes it possible to login to
3323          BASE if the external authentication server is down.
3324        </para>
3325       
3326        <note>
3327          <para>
3328          An external authentication server can only be used to grant or deny
3329          a user access to BASE. It cannot be used to give a user permissions,
3330          or put a user into groups or different roles inside BASE.
3331          </para>
3332        </note>
3333
3334        <para>
3335          The external authentication service is only used when a user logs in.
3336          Now, one or more of several things can happen:
3337         
3338          <itemizedlist>
3339          <listitem>
3340            <para>
3341              The ROOT user is logging on. Internal authentication is always
3342              used for the root user and the authenticator plug-in is never
3343              used.
3344            </para>
3345          </listitem>
3346
3347          <listitem>
3348            <para>
3349              The login is correct and the user is already known to BASE.
3350              If the plug-in supports extra information (name, email, phone, etc.)
3351              and the <property>auth.synchronize</property> setting
3352              is <constant>TRUE</constant> the extra information is copied to
3353              the BASE server.
3354            </para>
3355          </listitem>
3356         
3357          <listitem>
3358            <para>
3359              The login is correct, but the user is not known to BASE. This happens
3360              the first time a user logs in. BASE will create
3361              a new user account. If the driver supports extra information, it
3362              is copied to the BASE server (even if <property>auth.synchronize</property> 
3363              is not set). The new user account will get the default quota
3364              and be added to the all roles and groups which has been
3365              marked as <emphasis>default</emphasis>.
3366             
3367              <note>
3368              <para>
3369                Prior to BASE 2.4 it was hardcoded to add the new user to the
3370                <emphasis>Users</emphasis> role only.
3371              </para>
3372              </note>
3373            </para>
3374          </listitem>
3375
3376          <listitem>
3377            <para>
3378              If password caching is enabled, the password is copied to BASE.
3379              If an expiration timeout has been set, an expiration date
3380              will be calculated and set on the user account. The expiration
3381              date is only checked when the external authentication server is
3382              down.
3383            </para>
3384          </listitem>
3385
3386          <listitem>
3387            <para>
3388              The authentication server says that the login is invalid or
3389              the password is incorrect. The user will not be logged in.
3390              If a user account with the specified login already exists in
3391              BASE, it will be disabled.
3392            </para>
3393          </listitem>
3394         
3395          <listitem>
3396            <para>
3397              The authentication driver says that something else is wrong.
3398              If password caching is enabled, internal authentication will be
3399              used. Otherwise the user will not be logged in. An already
3400              existing account is not modified or disabled.
3401            </para>
3402          </listitem>
3403          </itemizedlist>
3404         
3405          <note>
3406            <para>
3407            The <guilabel>Encrypt password</guilabel> option that is
3408            available on the login page does not work with external
3409            authentication. The simple reason is that the password is
3410            encrypted with a one-way algorithm making it impossible to
3411            call <methodname>Authenticator.authenticate()</methodname>.
3412            </para>
3413          </note>
3414         
3415        </para>
3416      </sect3>
3417     
3418      <sect3 id="plugin_developer.other.authentication.authenticator">
3419        <title>The Authenticator interface</title>
3420       
3421        <para>
3422          To be able to use external authentication you must create a class
3423          that implements the
3424          <interfacename docapi="net.sf.basedb.core.authentication">net.sf.based.core.authentication.Authenticator</interfacename> 
3425          interface. Specify the name of the class in the <property>auth.driver</property> 
3426          setting in <filename>base.config</filename> and
3427          its initialisation parameters in the <property>auth.init</property> setting.
3428        </para>
3429       
3430        <para>
3431          Your class must have a public no-argument constructor. The BASE
3432          application will create only one instance of the class for lifetime
3433          of the BASE server. It must be thread-safe since it may be invoked by
3434          multiple threads at the same time. Here are the methods that you must
3435          implement
3436        </para>
3437       
3438        <variablelist>
3439        <varlistentry>
3440          <term>
3441            <methodsynopsis language="java">
3442              <modifier>public</modifier>
3443              <void/>
3444              <methodname>init</methodname>
3445              <methodparam>
3446                <type>String</type>
3447                <parameter>settings</parameter>
3448              </methodparam>
3449              <exceptionname>AuthenticationException</exceptionname>
3450            </methodsynopsis>
3451          </term>
3452          <listitem>
3453            <para>
3454            This method is called just after the object has been created with its argument
3455            taken from the <property>auth.init</property> setting in your <filename>base.config</filename> 
3456            file. This method is only called once for an instance of the object. The syntax and meaning of
3457            the parameter is driver-dependent and should be documented by the plug-in.
3458            It is irrelevant for the BASE core.
3459            </para>
3460          </listitem>
3461        </varlistentry>
3462       
3463        <varlistentry>
3464          <term>
3465            <methodsynopsis language="java">
3466              <modifier>public</modifier>
3467              <type>boolean</type>
3468              <methodname>supportsExtraInformation</methodname>
3469            </methodsynopsis>
3470          </term>
3471          <listitem>
3472            <para>
3473            This method should simply return <constant>TRUE</constant> or <constant>FALSE</constant> 
3474            depending on if the plug-in supports extra user information or not. The only required
3475            information about a user is a unique ID and the login. Extra information includes
3476            name, address, phone, email, etc.
3477            </para>
3478          </listitem>       
3479        </varlistentry>
3480
3481        <varlistentry>
3482          <term>
3483            <methodsynopsis language="java">
3484              <modifier>public</modifier>
3485              <type>AuthenticationInformation</type>
3486              <methodname>authenticate</methodname>
3487              <methodparam>
3488                <type>String</type>
3489                <parameter>login</parameter>
3490              </methodparam>
3491              <methodparam>
3492                <type>String</type>
3493                <parameter>password</parameter>
3494              </methodparam>
3495              <exceptionname>UnknownLoginException</exceptionname>
3496              <exceptionname>InvalidPasswordException</exceptionname>
3497              <exceptionname>AuthenticationException</exceptionname>
3498            </methodsynopsis>
3499          </term>
3500          <listitem>
3501            <para>
3502            Try to authenticate a login/password combination. The plug-in should return
3503            an <classname docapi="net.sf.basedb.core.authentication">AuthenticationInformation</classname> object if the
3504            authentication is successful or throw an exception if not.
3505           
3506            There are three exceptions to choose from:
3507           
3508            <itemizedlist>
3509            <listitem>
3510              <para>
3511              <exceptionname>UnknownLoginException</exceptionname>:
3512              This exception should be thrown if the login is not known to the
3513              external authentication system.
3514              </para>
3515            </listitem>
3516
3517            <listitem>
3518              <para>
3519              <exceptionname>InvalidPasswordException</exceptionname>:
3520              This exception should be thrown if the login is known but the
3521              password is invalid. In case it is considered a security issue
3522              to reveal that a login exists, the plugin may throw an
3523              <exceptionname>UnknowLoginException</exceptionname> instead.
3524              </para>
3525            </listitem>
3526           
3527            <listitem>
3528              <para>
3529              <exceptionname>AuthenticationException</exceptionname>:
3530              In case there is another problem, such as the authentication service
3531              being down. This exception triggers the use of cached passwords
3532              if caching has been enabled.
3533              </para>
3534            </listitem>
3535            </itemizedlist>
3536            </para>
3537          </listitem>       
3538        </varlistentry>
3539        </variablelist> 
3540      </sect3>
3541     
3542      <sect3 id="plugin_developer.other.authentication.settings">
3543        <title>Configuration settings</title>
3544     
3545        <para>
3546          The configuration settings for the authentication driver are located
3547          in the <filename>base.config</filename> file.
3548          Here is an overview of the settings. For more information read
3549          <xref linkend="appendix.base.config.authentication" />.
3550        </para>
3551       
3552        <variablelist>
3553        <varlistentry>
3554          <term><property>auth.driver</property></term>
3555          <listitem>
3556            <para>
3557            The class name of the authentication plug-in.
3558            </para>
3559          </listitem>
3560        </varlistentry>
3561
3562        <varlistentry>
3563          <term><property>auth.init</property></term>
3564          <listitem>
3565            <para>
3566            Initialisation parameters sent to the plug-in when calling the
3567            <methodname>Authenticator.init()</methodname> method.
3568            </para>
3569          </listitem>
3570        </varlistentry>
3571       
3572        <varlistentry>
3573          <term><property>auth.synchronize</property></term>
3574          <listitem>
3575            <para>
3576            If extra user information is synchronized at login time or not.
3577            This setting is ignored if the driver does not support extra information.
3578            </para>
3579          </listitem>
3580        </varlistentry>
3581       
3582        <varlistentry>
3583          <term><property>auth.cachepasswords</property></term>
3584          <listitem>
3585            <para>
3586              If passwords should be cached by BASE or not. If the passwords are
3587              cached a user may login to BASE even if the external authentication
3588              server is down.
3589            </para>
3590          </listitem>
3591        </varlistentry>
3592       
3593        <varlistentry>
3594          <term><property>auth.daystocache</property></term>
3595          <listitem>
3596            <para>
3597              How many days to cache the passwords if caching has been enabled.
3598              A value of 0 caches the passwords for ever.     
3599            </para>
3600          </listitem>
3601        </varlistentry>
3602        </variablelist>
3603      </sect3>
3604     
3605    </sect2>
3606   
3607    <sect2 id="plugin_developer.other.secondary">
3608      <title>Secondary file storage plugins</title>
3609     
3610      <sect3 id="plugin_developer.other.secondary.vsprimary">
3611        <title>Primary vs. secondary storage</title>
3612        <para>
3613          BASE has support for storing files in two locations, the primary storage and
3614          the secondary storage. The primary storage is always disk-based and must be
3615          accessible by the BASE server as a path on the file system. The path to the
3616          primary storage is configured by the <varname>userfiles</varname> setting in the
3617          <filename>base.config</filename> file. The primary storage is internal to
3618          the core. Client applications don't get access to read or manipulate the
3619          files directly from the file system.
3620        </para>
3621       
3622        <para>
3623          The secondary storage can be anything that can store files. It could, for
3624          example, be another directory, a remote FTP server, or a tape based archiving
3625          system. A file located in the secondary storage is not accessible by the
3626          core, client applications or plug-ins. The secondary storage can only be accessed
3627          by the secondary storage controller. The core (and client) applications uses
3628          flags on the file items to handle the interaction with the secondary storage.
3629        </para>
3630       
3631        <para>
3632          Each file has an <property>action</property> attribute which default's to
3633          <constant>File.Action.NOTHING</constant>. It can take two other values:
3634        </para>
3635       
3636        <orderedlist>
3637        <listitem>
3638          <para>
3639          <constant>File.Action.MOVE_TO_SECONDARY</constant>
3640          </para>
3641        </listitem>
3642        <listitem>
3643          <para>
3644          <constant>File.Action.MOVE_TO_PRIMARY</constant>
3645          </para>
3646        </listitem>
3647        </orderedlist>
3648       
3649        <para>
3650          All files with the action attribute set to <constant>MOVE_TO_SECONDARY</constant> 
3651          should be moved to the secondary storage by the controller, and all files
3652          with the action attribute set to <constant>MOVE_TO_PRIMARY</constant> should be
3653          brought back to primary storage.
3654        </para>
3655       
3656        <para>
3657          The moving of files between primary and secondary storage doesn't happen
3658          immediately. It is up to the server administrator to configure how often and
3659          at what times the controller should check for files that should be moved.
3660          This is configured by the <varname>secondary.storage.interval</varname> 
3661          and <varname>secondary.storage.time</varname> settings in the
3662          <filename>base.config</filename> file.       
3663        </para>
3664      </sect3>
3665     
3666      <sect3 id="plugin_developer.other.secondary.interface">
3667        <title>The SecondaryStorageController interface</title>
3668       
3669        <para>
3670          All you have to do to create a secondary storage controller is to
3671          create a class that implements the
3672          <interfacename docapi="net.sf.basedb.core">net.sf.basedb.core.SecondaryStorageController</interfacename>
3673          interface. In your <filename>base.config</filename> file you then specify the
3674          class name in the <varname>secondary.storage.driver</varname> setting and its
3675          initialisation parameters in the <varname>secondary.storage.init</varname> setting.
3676        </para>
3677
3678        <para>
3679          Your class must have a public no-argument constructor.
3680          The BASE application will create only one instance of the class for
3681          lifetime of the BASE server. Here are the methods that you must implement:
3682        </para>
3683       
3684        <variablelist>
3685        <varlistentry>
3686          <term>
3687          <methodsynopsis language="java">
3688            <modifier>public</modifier>
3689            <void />
3690            <methodname>init</methodname>
3691            <methodparam>
3692              <type>String</type>
3693              <parameter>settings</parameter>
3694            </methodparam>
3695          </methodsynopsis>
3696          </term>
3697          <listitem>
3698            <para>
3699            This method is called just after the object has been created with its argument
3700            taken from the <varname>secondary.storage.init</varname> setting in your
3701            <filename>base.config</filename> file. This method is only called once for
3702            an object.
3703            </para>
3704          </listitem>
3705        </varlistentry>
3706        <varlistentry>
3707          <term>
3708          <methodsynopsis language="java">
3709            <modifier>public</modifier>
3710            <void />
3711            <methodname>run</methodname>
3712          </methodsynopsis>
3713          </term>
3714          <listitem>
3715            <para>
3716            This method is called whenever the core thinks it is time to do some
3717            management of the secondary storage. How often the <methodname>run()</methodname>
3718            method is called is controlled by the <varname>secondary.storage.interval</varname>
3719            and <varname>secondary.storage.time</varname> settings in the
3720            <filename>base.config</filename> file.
3721            When this method is called the controller should:
3722            </para>
3723           
3724            <itemizedlist>
3725            <listitem>
3726              <para>
3727              Move all files which has <constant>action=MOVE_TO_SECONDARY</constant> to
3728              the secondary storage. When the file has been moved call
3729              <methodname>File.setLocation(Location.SECONDARY)</methodname> to tell the
3730              core that the file is now in the secondary storage. You should also call
3731              <methodname>File.setAction(File.Action.NOTHING)</methodname> to reset the
3732              action attribute.
3733              </para>
3734            </listitem>
3735           
3736            <listitem>
3737              <para>
3738              Restore all files which has <constant>action=MOVE_TO_PRIMARY</constant>.
3739              The core will set the location attribute automatically, but you should
3740              call <methodname>File.setAction(File.Action.NOTHING)</methodname> to reset
3741              the action attribute.
3742              </para>
3743            </listitem>
3744           
3745            <listitem>
3746              <para>
3747              Delete all files from the secondary storage that are not present
3748              in the database with <constant>location=Location.SECONDARY</constant>.
3749              This includes files which has been deleted and files that have been
3750              moved offline or re-uploaded.
3751              </para>
3752            </listitem>
3753           
3754            </itemizedlist>
3755           
3756            <para>
3757              As a final act the method should send a message to each user owning
3758              files that has been moved from one location to the other. The message
3759              should include a list of files that has been moved to the secondary
3760              storage and a list of files moved from the secondary storage and a
3761              list of files that has been deleted due to some of the reasons above.
3762            </para>
3763          </listitem>
3764        </varlistentry>       
3765       
3766        <varlistentry>
3767          <term>
3768          <methodsynopsis language="java">
3769            <modifier>public</modifier>
3770            <void />
3771            <methodname>close()</methodname>
3772          </methodsynopsis>
3773          </term>
3774          <listitem>
3775            <para>
3776            This method is called when the server is closing down. After this the object
3777            is never used again.
3778            </para>
3779          </listitem>
3780        </varlistentry>
3781        </variablelist>
3782      </sect3>
3783     
3784      <sect3 id="plugin_developer.other.secondary.settings">
3785        <title>Configuration settings</title>
3786       
3787        <para>
3788        The configuration settings for the secondary storage controller is located in the
3789        <filename>base.config</filename> file. Here is an overview of the settings.
3790        For more information read <xref linkend="appendix.base.config" />.
3791        </para>
3792       
3793        <variablelist>
3794        <varlistentry>
3795          <term><property>secondary.storage.driver</property></term>
3796          <listitem>
3797            <para>
3798            The class name of the secondary storage plug-in.
3799            </para>
3800          </listitem>
3801        </varlistentry>
3802        <varlistentry>
3803          <term><property>secondary.storage.init</property></term>
3804          <listitem>
3805            <para>
3806             Initialisation parameters sent to the plug-in by calling the
3807             <methodname>init()</methodname> method.
3808            </para>
3809          </listitem>
3810        </varlistentry>
3811        <varlistentry>
3812          <term><property>secondary.storage.interval</property></term>
3813          <listitem>
3814            <para>
3815             Interval in seconds between each execution of the secondary storage
3816             controller plug-in.
3817            </para>
3818          </listitem>
3819        </varlistentry>
3820        <varlistentry>
3821          <term><property>secondary.storage.time</property></term>
3822          <listitem>
3823            <para>
3824              Time points during the day when the secondary storage controller plugin
3825              should be executed.
3826            </para>
3827          </listitem>
3828        </varlistentry>
3829        </variablelist>
3830      </sect3>
3831    </sect2>
3832   
3833    <sect2 id="plugin_developer.other.unpacker">
3834      <title>File unpacker plug-ins</title>
3835      <para>
3836        The BASE web client has integrated support for unpacking of
3837        compressed files. See <xref linkend="file_system.handling.upload" />.
3838        Behind the scenes, this support is provided by plug-ins. The standard
3839        BASE distribution comes with support for ZIP files
3840        (<classname docapi="net.sf.basedb.plugins">net.sf.basedb.plugins.ZipFileUnpacker</classname>)
3841        and TAR files (<classname docapi="net.sf.basedb.plugins">net.sf.basedb.plugins.TarFileUnpacker</classname>).
3842      </para>
3843      <para>
3844        To add support for additional compressed formats you have to create a plug-in that
3845        implements the <interfacename docapi="net.sf.basedb.util.zip">net.sf.basedb.util.zip.FileUnpacker</interfacename>
3846        interface. The best way to do this is to extend the
3847        <classname docapi="net.sf.basedb.util.zip">net.sf.basedb.util.zip.AbstractFileUnpacker</classname> which
3848        implements all methods in the <interfacename docapi="net.sf.basedb.core.plugin">Plugin</interfacename>
3849        and <interfacename docapi="net.sf.basedb.core.plugin">InteractivePlugin</interfacename>
3850        interfaces except <methodname>Plugin.getAbout()</methodname>. This leaves
3851        you with the actual unpacking of the files as the only thing to implement.
3852      </para>
3853     
3854      <note>
3855        <title>No support for configurations</title>
3856        The integrated upload in the web interface only works with plug-ins that
3857        does not require a configuration to run.
3858      </note>
3859     
3860      <variablelist>
3861        <title>Methods in the <interfacename docapi="net.sf.basedb.util.zip">FileUnpacker</interfacename> interface</title>
3862        <varlistentry>
3863          <term>
3864            <methodsynopsis language="java">
3865              <modifier>public</modifier>
3866              <type>String</type>
3867              <methodname>getFormatName</methodname>
3868            </methodsynopsis>
3869          </term>
3870          <listitem>
3871            <para>
3872            Return a short string naming the file format. For example:
3873            <constant>ZIP files</constant> or <constant>TAR files</constant>.
3874            </para>
3875          </listitem>
3876        </varlistentry>
3877           
3878        <varlistentry>
3879          <term>
3880            <methodsynopsis language="java">
3881              <modifier>public</modifier>
3882              <type>Set&lt;String&gt;</type>
3883              <methodname>getExtensions</methodname>
3884            </methodsynopsis>
3885          </term>
3886          <listitem>
3887            <para>
3888            Return a set of strings with the file extensions that
3889            are most commonly used with the compressed file format.
3890            For example: <constant>[zip, jar]</constant>. Do not include
3891            the dot in the extensions. The web client and the
3892            <methodname>AbstractFlatFileUnpacker.isInContext()</methodname> method
3893            will use this information to automatically guess which plug-in to
3894            use for unpacking the files.
3895            </para>
3896          </listitem>
3897        </varlistentry>
3898       
3899        <varlistentry>
3900          <term>
3901            <methodsynopsis language="java">
3902              <modifier>public</modifier>
3903              <type>Set&lt;String&gt;</type>
3904              <methodname>getMimeTypes</methodname>
3905            </methodsynopsis>
3906          </term>
3907          <listitem>
3908            <para>
3909            Return a set of string with the MIME types that commonly used with
3910            the compressed file format. For example:
3911            <constant>[application/zip, application/java-archive]</constant>.
3912            This information is used by the
3913            <methodname>AbstractFlatFileUnpacker.isInContext()</methodname> 
3914            method to automatically guess which plug-in to use for unpacking
3915            the files.
3916            </para>
3917          </listitem>
3918        </varlistentry>
3919       
3920        <varlistentry>
3921          <term>
3922            <methodsynopsis language="java">
3923              <modifier>public</modifier>
3924              <type>int</type>
3925              <methodname>unpack</methodname>
3926              <methodparam>
3927                <type>DbControl</type>
3928                <parameter>dc</parameter>
3929              </methodparam>
3930              <methodparam>
3931                <type>Directory</type>
3932                <parameter>dir</parameter>
3933              </methodparam>
3934              <methodparam>
3935                <type>InputStream</type>
3936                <parameter>in</parameter>
3937              </methodparam>
3938              <methodparam>
3939                <type>boolean</type>
3940                <parameter>overwrite</parameter>
3941              </methodparam>
3942              <methodparam>
3943                <type>AbsoluteProgressReporter</type>
3944                <parameter>progress</parameter>
3945              </methodparam>
3946              <exceptionname>IOException</exceptionname>
3947              <exceptionname>BaseException</exceptionname>
3948            </methodsynopsis>
3949          </term>
3950          <listitem>
3951            <para>
3952            Unpack the files and store them in the BASE file system.
3953           
3954            <itemizedlist>
3955            <listitem>
3956              <para>
3957              Do not <methodname>close()</methodname> or
3958              <methodname>commit()</methodname> the
3959              <classname docapi="net.sf.basedb.core">DbControl</classname> passed to this method.
3960              This is done automatically by the
3961              <classname docapi="net.sf.basedb.util.zip">AbstractFileUnpacker</classname> or by the web client.
3962              </para>
3963            </listitem>
3964           
3965            <listitem>
3966              <para>
3967              The <varname>dir</varname> parameter is the root directory where
3968              the unpacked files should be placed. If the compressed file
3969              contains subdirectories the plug-in must create those subdirectories
3970              unless they already exists.
3971              </para>
3972            </listitem>
3973           
3974            <listitem>
3975              <para>
3976              If the <varname>overwrite</varname> parameter is
3977              <constant>FALSE</constant> no existing file should be overwritten
3978              unless the file is <systemitem>OFFLINE</systemitem>.
3979              </para>
3980            </listitem>
3981           
3982            <listitem>
3983              <para>
3984              The <varname>in</varname> parameter is the stream
3985              containing the compressed data. The stream may come
3986              directly from the web upload or from an existing
3987              file in the BASE file system.
3988              </para>
3989            </listitem>
3990           
3991            <listitem>
3992              <para>
3993              The <varname>progress</varname> parameter, if not
3994              <constant>null</constant>, should be used to report the
3995              progress back to the calling code. The plug-in should count
3996              the number of bytes read from the <varname>in</varname>
3997              stream. If it is not possible by other means the stream can
3998              be wrapped by a <classname docapi="net.sf.basedb.util">net.sf.basedb.util.InputStreamTracker</classname>
3999              object which has a <methodname>getNumRead()</methodname> method.
4000              </para>
4001            </listitem>
4002            </itemizedlist>
4003           
4004            </para>
4005          </listitem>
4006        </varlistentry>
4007      </variablelist>
4008     
4009      <para>
4010        When the compressed file is uncompressed during the file upload
4011        from the web interface, the call sequence to the plug-in is slightly
4012        altered from the standard call sequence described in
4013        <xref linkend="plugin_developer.api.callsequence.execute" />.
4014       
4015        <itemizedlist>
4016        <listitem>
4017          <para>
4018          After the plug-in instance has been created, the
4019          <methodname>Plugin.init()</methodname> method is called with <constant>null</constant>
4020          values for both the <varname>configuration</varname> and <varname>job</varname>
4021          parameters.
4022          </para>
4023        </listitem>
4024       
4025        <listitem>
4026          <para>
4027          Then, the <methodname>unpack()</methodname> method is called. The
4028          <methodname>Plugin.run()</methodname> method is never called in this case.
4029          </para>
4030        </listitem>
4031       
4032        </itemizedlist>
4033       
4034      </para>
4035     
4036    </sect2>
4037   
4038    <sect2 id="plugin_developer.other.packer">
4039      <title>File packer plug-ins</title>
4040   
4041      <para>
4042        BASE has support for compressing and downloading a set of selected files and/or
4043        directories. This functionality is provided by a plug-in, the
4044        <classname docapi="net.sf.basedb.plugins">PackedFileExporter</classname>. This plug-in doesn't do the actual
4045        packing itself. This is delegated to classes implementing the
4046        <interfacename docapi="net.sf.basedb.util.zip">net.sf.basedb.util.zip.FilePacker</interfacename> interface.
4047      </para>
4048     
4049      <para>
4050        BASE ships with a number of packing methods, including ZIP and TAR. To
4051        add support for other methods you have to provide an implementation
4052        of the <interfacename docapi="net.sf.basedb.util.zip">FilePacker</interfacename>
4053        interface. Then, create a new configuration for the <classname docapi="net.sf.basedb.plugins">PackedFileExporter</classname>
4054        and enter the name of your class in the configuration wizard.
4055      </para>
4056     
4057      <para>
4058        The <interfacename docapi="net.sf.basedb.util.zip">FilePacker</interfacename> interface is not a regular
4059        plug-in interface (ie. it is not a subinterface to
4060        <interfacename docapi="net.sf.basedb.core.plugin">Plugin</interfacename>). This means that you don't have to
4061        mess with configuration or job parameters. Another difference is that your
4062        class must be installed in Tomcat's classpath (ie. in one of the
4063        <filename>WEB-INF/classes</filename> or <filename>WEB-INF/lib</filename>
4064        folders).
4065      </para>
4066     
4067      <variablelist>
4068        <title>Methods in the <interfacename docapi="net.sf.basedb.util.zip">FilePacker</interfacename> interface</title>
4069        <varlistentry>
4070          <term>
4071            <methodsynopsis language="java">
4072              <modifier>public</modifier>
4073              <type>String</type>
4074              <methodname>getDescription</methodname>
4075            </methodsynopsis>
4076          </term>
4077          <listitem>
4078            <para>
4079            Return a short description the file format that is suitable for use
4080            in dropdown lists in client applications. For example:
4081            <constant>Zip-archive (.zip)</constant> or <constant>TAR-archive (.tar)</constant>.
4082            </para>
4083          </listitem>
4084        </varlistentry>
4085        <varlistentry>
4086          <term>
4087            <methodsynopsis language="java">
4088              <modifier>public</modifier>
4089              <type>String</type>
4090              <methodname>getFileExtension</methodname>
4091            </methodsynopsis>
4092          </term>
4093          <listitem>
4094            <para>
4095            Return the default file extension of the packed format. The returned
4096            value should not include the dot. For example:
4097            <constant>zip</constant> or <constant>tar</constant>.
4098            </para>
4099          </listitem>
4100        </varlistentry>
4101        <varlistentry>
4102          <term>
4103            <methodsynopsis language="java">
4104              <modifier>public</modifier>
4105              <type>String</type>
4106              <methodname>getMimeType</methodname>
4107            </methodsynopsis>
4108          </term>
4109          <listitem>
4110            <para>
4111            Return the standard MIME type of the packed file format.
4112            For example:
4113            <constant>application/zip</constant> or <constant>application/x-tar</constant>.
4114            </para>
4115          </listitem>
4116        </varlistentry>
4117        <varlistentry>
4118          <term>
4119            <methodsynopsis language="java">
4120              <modifier>public</modifier>
4121              <void />
4122              <methodname>setOutputStream</methodname>
4123              <methodparam>
4124                <type>OutputStream</type>
4125                <parameter>out</parameter>
4126              </methodparam>
4127              <exceptionname>IOException</exceptionname>
4128            </methodsynopsis>
4129          </term>
4130          <listitem>
4131            <para>
4132            Sets the outputstream that the packer should write the packed
4133            files to.
4134            </para>
4135          </listitem>
4136        </varlistentry>
4137        <varlistentry>
4138          <term>
4139            <methodsynopsis language="java">
4140              <modifier>public</modifier>
4141              <void />
4142              <methodname>pack</methodname>
4143              <methodparam>
4144                <type>String</type>
4145                <parameter>entryName</parameter>
4146              </methodparam>
4147              <methodparam>
4148                <type>InputStream</type>
4149                <parameter>in</parameter>
4150              </methodparam>
4151              <methodparam>
4152                <type>long</type>
4153                <parameter>size</parameter>
4154              </methodparam>
4155              <methodparam>
4156                <type>long</type>
4157                <parameter>lastModified</parameter>
4158              </methodparam>
4159              <exceptionname>IOException</exceptionname>
4160            </methodsynopsis>
4161          </term>
4162          <listitem>
4163            <para>
4164            Add another file or directory to the packed file. The
4165            <parameter>entryName</parameter> is the name of the new entry, including
4166            path information. The <parameter>in</parameter> is the stream to read
4167            the file data from. If <parameter>in</parameter> is <constant>null</constant>
4168            then the entry denotes a directory. The <parameter>size</parameter> parameter
4169            gives the size in bytes of the file (zero for empty files or directories).
4170            The <parameter>lastModified</parameter>
4171            is that time the file was last modified or 0 if not known.
4172            </para>
4173          </listitem>
4174        </varlistentry>
4175        <varlistentry>
4176          <term>
4177            <methodsynopsis language="java">
4178              <modifier>public</modifier>
4179              <void />
4180              <methodname>close</methodname>
4181              <exceptionname>IOException</exceptionname>
4182            </methodsynopsis>
4183          </term>
4184          <listitem>
4185            <para>
4186            Finish the packing. The packer should release any resources, flush
4187            all data and close all output streams, including the <varname>out</varname> stream
4188            set in the <methodname>setOutputStream</methodname> method.
4189            </para>
4190          </listitem>
4191        </varlistentry>
4192       
4193      </variablelist>
4194     
4195    </sect2>
4196   
4197    <sect2 id="plugin_developer.other.datafiles">
4198      <title>File validator and metadata reader plug-ins</title>
4199   
4200      <itemizedlist>
4201        <title>See also</title>
4202        <listitem><xref linkend="core_api.data_in_files" /></listitem>
4203        <listitem><xref linkend="data_api.platforms" /></listitem>
4204      </itemizedlist>
4205   
4206   
4207      <para>
4208        In those cases where files are used to store data instead
4209        of importing it to the database, BASE can use plug-ins to
4210        check that the supplied files are valid and also to extract
4211        metadata from the files. For example, the
4212        <classname docapi="net.sf.basedb.core.filehandler">net.sf.basedb.core.filehandler.CelFileHandler</classname>
4213        is used to check if a file is a valid Affymetrix CEL file and
4214        to extract data headers and the number of spots from it.
4215      </para>
4216     
4217      <para>
4218        The validator and metadata reader plug-ins are not regular plug-ins
4219        (ie. they don't have to implement the <interfacename docapi="net.sf.basedb.core.plugin">Plugin</interfacename>
4220        interface). This means that you don't have to mess with configuration or
4221        job parameters.
4222      </para>
4223     
4224      <para>
4225        Validator plug-ins must implement the
4226        <interfacename docapi="net.sf.basedb.core.filehandler">net.sf.basedb.core.filehandler.DataFileHandler</interfacename>
4227        and <interfacename docapi="net.sf.basedb.core.filehandler">net.sf.basedb.core.filehandler.DataValidator</interfacename>
4228        interfaces. Metadata reader plug-ins should implement the
4229        <interfacename docapi="net.sf.basedb.core.filehandler">net.sf.basedb.core.filehandler.DataFileHandler</interfacename>
4230        and <interfacename docapi="net.sf.basedb.core.filehandler">net.sf.basedb.core.filehandler.DataFileMetadataReader</interfacename>
4231        interfaces.
4232      </para>
4233
4234      <note>
4235        <para>
4236        Meta data extraction can only be done if the file has first been
4237        validated. We recommend that metadata reader plug-ins also takes the role as
4238        validator plug-ins. This will make BASE re-use the same object instance
4239        and the file doesn't have to be parsed twice.
4240        </para>
4241      </note>
4242
4243      <important>
4244        <title>
4245          Always extend the
4246          <classname docapi="net.sf.basedb.core.filehandler">net.sf.basedb.core.filehandler.AbstractDataFileHandler</classname>
4247          class
4248        </title>
4249        <para>
4250          We consider the mentioned interface to be part of the public API only
4251          from the caller side, not from the implementor side. Thus, we may
4252          add methods to those interfaces in the future without prior notice.
4253          The <classname docapi="net.sf.basedb.core.filehandler">AbstractDataFileHandler</classname> will provide default
4254          implementations of the new methods in order to not break existing
4255          plug-ins.
4256        </para>
4257      </important>
4258     
4259      <variablelist>
4260        <title>Methods in the <interfacename docapi="net.sf.basedb.core.filehandler">DataFileHandler</interfacename> interface</title>
4261        <varlistentry>
4262          <term>
4263            <methodsynopsis language="java">
4264              <modifier>public</modifier>
4265              <void/>
4266              <methodname>setFile</methodname>
4267              <methodparam>
4268                <type>FileSetMember</type>
4269                <parameter>member</parameter>
4270              </methodparam>
4271            </methodsynopsis>
4272          </term>
4273          <listitem>
4274            <para>
4275            Sets the file that is going to be validated or used for metadata
4276            extraction. If the same plug-in can be used for validating
4277            more than one type of file, this method will be called
4278            one time for each file that is present in the file set.
4279            </para>
4280          </listitem>
4281        </varlistentry>
4282       
4283        <varlistentry>
4284          <term>
4285            <methodsynopsis language="java">
4286              <modifier>public</modifier>
4287              <void/>
4288              <methodname>setItem</methodname>
4289              <methodparam>
4290                <type>FileStoreEnabled</type>
4291                <parameter>item</parameter>
4292              </methodparam>
4293            </methodsynopsis>
4294          </term>
4295          <listitem>
4296            <para>
4297            Sets the item that the files belong to. This method is only
4298            called once.
4299            </para>
4300          </listitem>
4301        </varlistentry>
4302      </variablelist>
4303     
4304      <variablelist>
4305        <title>Methods in the <interfacename docapi="net.sf.basedb.core.filehandler">DataFileValidator</interfacename> interface</title>
4306        <varlistentry>
4307          <term>
4308            <methodsynopsis language="java">
4309              <modifier>public</modifier>
4310              <void/>
4311              <methodname>validate</methodname>
4312              <methodparam>
4313                <type>DbControl</type>
4314                <parameter>dc</parameter>
4315              </methodparam>
4316              <exceptionname>InvalidDataException</exceptionname>
4317              <exceptionname>InvalidRelationException</exceptionname>
4318            </methodsynopsis>
4319          </term>
4320          <listitem>
4321            <para>
4322              Validate the file. The file is valid if this method returns
4323              sucessfully. If the file is not valid an
4324              <exceptionname>InvalidDataException</exceptionname> should be
4325              thrown. Note that BASE will still accept the file, but will indicate
4326              the failure with a flag and also keep the message of the exception in the
4327              database to remind the user of the failure.
4328            </para>
4329            <para>
4330              The <exceptionname>InvalidRelationException</exceptionname>
4331              should be used to indicate a partial success/partial failure,
4332              where the file as such is a valid file, but in relation to
4333              other files it is not. For example, we may assign a valid CEL
4334              file to a raw bioassay, but the chip type doesn't match
4335              the chip type of the CDF file of the related array design.
4336              This exception will also allow metadata to be extracted from
4337              the file.
4338            </para>
4339          </listitem>
4340        </varlistentry>
4341      </variablelist>
4342
4343      <variablelist>
4344        <title>Methods in the <interfacename docapi="net.sf.basedb.core.filehandler">DataFileMetadataReader</interfacename> interface</title>
4345        <varlistentry>
4346          <term>
4347            <methodsynopsis language="java">
4348              <modifier>public</modifier>
4349              <void/>
4350              <methodname>extractMetadata</methodname>
4351              <methodparam>
4352                <type>DbControl</type>
4353                <parameter>dc</parameter>
4354              </methodparam>
4355            </methodsynopsis>
4356          </term>
4357          <listitem>
4358            <para>
4359              Extract metadata from the file. It is up to the plug-in
4360              to decide what to extract and how to store it.
4361              The <classname docapi="net.sf.basedb.core.filehandler">CelFileHandler</classname> will, for
4362              example, extract headers and the number of spots from the file
4363              and store it with the raw bioassay.
4364            </para>
4365          </listitem>
4366        </varlistentry>
4367        <varlistentry>
4368          <term>
4369            <methodsynopsis language="java">
4370              <modifier>public</modifier>
4371              <void/>
4372              <methodname>resetMetadata</methodname>
4373              <methodparam>
4374                <type>DbControl</type>
4375                <parameter>dc</parameter>
4376              </methodparam>
4377            </methodsynopsis>
4378          </term>
4379          <listitem>
4380            <para>
4381              Remove all metadata that the plug-in usually can extract.
4382              This method is called if a file is unlinked from an item
4383              or if the validation fails. It is important that the
4384              plug-in cleans up everything so that data from a
4385              previous file doesn't remain in the database.
4386            </para>
4387          </listitem>
4388        </varlistentry>
4389      </variablelist>
4390
4391      <variablelist>
4392        <title>Methods in the <classname docapi="net.sf.basedb.core.filehandler">AbstractDataFileHandler</classname> class</title>
4393        <varlistentry>
4394          <term>
4395            <methodsynopsis language="java">
4396              <modifier>public</modifier>
4397              <type>FileStoreEnabled</type>
4398              <methodname>getItem</methodname>
4399            </methodsynopsis>
4400          </term>
4401          <listitem>
4402            <para>
4403              Get the item that was previously added to
4404              <methodname>setItem()</methodname>
4405            </para>
4406          </listitem>
4407        </varlistentry>
4408        <varlistentry>
4409          <term>
4410            <methodsynopsis language="java">
4411              <modifier>public</modifier>
4412              <type>FileSetMember</type>
4413              <methodname>getMember</methodname>
4414              <methodparam>
4415                <type>String</type>
4416                <parameter>dataFileTypeId</parameter>
4417              </methodparam>
4418            </methodsynopsis>
4419          </term>
4420          <listitem>
4421            <para>
4422              Get a file that was previously added to
4423              <methodname>setFile()</methodname>. The
4424              <parameter>dataFileTypeId</parameter> is
4425              the external ID of the <classname docapi="net.sf.basedb.core">DataFileType</classname>.
4426            </para>
4427          </listitem>
4428        </varlistentry>
4429      </variablelist>
4430
4431    </sect2>
4432   
4433    <sect2 id="plugin_developer.other.logging">
4434      <title>Logging plug-ins</title>
4435     
4436      <para>
4437        BASE provides a plug-in mechanism for logging changes that are made to items.
4438        This plug-in mechanism is not the same as the regular plug-in API. That is, you do not
4439        have worry about user interaction or implementing the <interfacename 
4440        docapi="net.sf.basedb.core.plugin">Plugin</interfacename> interface.
4441      </para>
4442     
4443      <para>
4444        The logging mechanism works on the data layer level and hooks into
4445        callbacks provided by Hibernate. <interfacename 
4446        docapi="net.sf.basedb.core.log">EntityLogger</interfacename>:s are used to
4447        extract relevant information from Hibernate and create log entries.
4448        While it is possible to have a generic logger it is usually better
4449        to have different implementations depending on the type of entity that
4450        was changed. For example, a change in a child item should, for usability
4451        reasons, be logged as a change in the parent item. Entity loggers
4452        are created by a <interfacename 
4453        docapi="net.sf.basedb.core.log">LogManagerFactory</interfacename>. All changes
4454        made in a single transaction are usually collected by a <interfacename 
4455        docapi="net.sf.basedb.core.log">LogManager</interfacename> which is also
4456        created by the factory.
4457      </para>
4458     
4459      <sect3 id="plugin_developer.other.logmanagerfactory">
4460        <title>The LogManagerFactory interface</title>
4461       
4462        <para>
4463          Which <interfacename 
4464          docapi="net.sf.basedb.core.log">LogManagerFactory</interfacename> to use
4465          is configured in <filename>base.config</filename> (See <xref 
4466          linkend="appendix.base.config.log"/>). A single factory instance is created
4467          when BASE starts and is used for the lifetime of the virtual machine. The
4468          factory implementation must of course be thread-safe. Here is a list of
4469          the methods the factory must implement:
4470        </para>
4471       
4472        <variablelist>
4473          <varlistentry>
4474            <term>
4475              <methodsynopsis language="java">
4476                <modifier>public</modifier>
4477                <type>LogManager</type>
4478                <methodname>getLogManager</methodname>
4479                <methodparam>
4480                  <type>LogControl</type>
4481                  <parameter>logControl</parameter>
4482                </methodparam>
4483              </methodsynopsis>
4484            </term>
4485            <listitem>
4486              <para>
4487              Creates a log manager for a single transaction. Since a transaction
4488              is not thread-safe the log manager implementation doesn't have to
4489              be either. The factory has the possibility to create new log managers
4490              for each transaction.
4491              </para>
4492            </listitem>
4493          </varlistentry>
4494         
4495          <varlistentry>
4496            <term>
4497              <methodsynopsis language="java">
4498                <modifier>public</modifier>
4499                <type>boolean</type>
4500                <methodname>isLoggable</methodname>
4501                <methodparam>
4502                  <type>Object</type>
4503                  <parameter>entity</parameter>
4504                </methodparam>
4505              </methodsynopsis>
4506            </term>
4507            <listitem>
4508              <para>
4509              Checks if changes to the given entity should be
4510              logged or not. For performance reasons, it usually makes sense to
4511              not log everything. For example, the database logger implementation
4512              only logs changes if the entity implements the <interfacename 
4513              docapi="net.sf.basedb.core.data">LoggableData</interfacename>
4514              interface. The return value of this method should be consistent
4515              with <methodname>getEntityLogger()</methodname>.
4516              </para>
4517            </listitem>
4518          </varlistentry>
4519
4520          <varlistentry>
4521            <term>
4522              <methodsynopsis language="java">
4523                <modifier>public</modifier>
4524                <type>EntityLogger</type>
4525                <methodname>getEntityLogger</methodname>
4526                <methodparam>
4527                  <type>LogManager</type>
4528                  <parameter>logManager</parameter>
4529                </methodparam>
4530                <methodparam>
4531                  <type>Object</type>
4532                  <parameter>entity</parameter>
4533                </methodparam>
4534              </methodsynopsis>
4535            </term>
4536            <listitem>
4537              <para>
4538              Create or get an entity logger that knows how to log
4539              changes to the given entity. If the entity should not be
4540              logged, <constant>null</constant> can be returned. This method
4541              is called for each modified item in the transaction.
4542              </para>
4543            </listitem>
4544          </varlistentry>
4545        </variablelist>
4546       
4547      </sect3>
4548     
4549      <sect3 id="plugin_developer.other.logmanager">
4550        <title>The LogManager interface</title>
4551       
4552        <para>
4553          A new log manager is created for each transaction. The log manager
4554          is responsible for collecting all changes made in the transaction
4555          and store those changes in the appropriate place. The interface doesn't define
4556          any methods for this collection, since each implementation may have
4557          very different needs.
4558        </para>
4559       
4560        <variablelist>
4561          <varlistentry>
4562            <term>
4563              <methodsynopsis language="java">
4564                <modifier>public</modifier>
4565                <type>LogControl</type>
4566                <methodname>getLogControl</methodname>
4567              </methodsynopsis>
4568            </term>
4569            <listitem>
4570              <para>
4571              Get the log control object that was supplied by the BASE core
4572              when the transaction was started. The log controller contains
4573              methods for accessing information about the transaction, such
4574              as the logged in user, executing plug-in, etc. It can also
4575              be used to execute queries against the database to get
4576              even more information.
4577              </para>
4578             
4579              <warning>
4580                <para>
4581                  Be careful about the queries that are executed by the log
4582                  controller. Since all logging code is executed at flush
4583                  time in callbacks from Hibernate we are not allowed to
4584                  use the regular session. Instead, all queries are sent
4585                  through the stateless session. The stateless session has no
4586                  caching functionality which means that Hibernate will use
4587                  extra queries to load associations. Our recommendation is
4588                  to avoid quires that return full entities, use scalar
4589                  queries instead to just load the values that are needed.
4590                </para>
4591              </warning>
4592            </listitem>
4593          </varlistentry>
4594          <varlistentry>
4595            <term>
4596              <methodsynopsis language="java">
4597                <modifier>public</modifier>
4598                <void/>
4599                <methodname>afterCommit</methodname>
4600              </methodsynopsis>
4601            </term>
4602            <term>
4603              <methodsynopsis language="java">
4604                <modifier>public</modifier>
4605                <void/>
4606                <methodname>afterRollback</methodname>
4607              </methodsynopsis>
4608            </term>
4609            <listitem>
4610              Called after a successful commit or after a rollback. Note
4611              that the connection to the database has been closed at this
4612              time and it is not possible to save any more information to
4613              it at this time.
4614            </listitem>
4615          </varlistentry>
4616        </variablelist>
4617      </sect3>
4618
4619      <sect3 id="plugin_developer.other.entitylogger">
4620        <title>The EntityLogger interface</title>
4621       
4622        <para>
4623          An entity logger is responsible for extracting the changes
4624          made to an entity and converting it to something that is useful
4625          as a log entry. In most cases, this is not very complicated, but
4626          in some cases, a change in one entity should actually be logged
4627          as a change in a different entity. For example, changes to
4628          annotations are handled by the <classname 
4629          docapi="net.sf.basedb.core.log.db">AnnotationLogger</classname> which
4630          which log it as a change on the parent item.
4631        </para>
4632       
4633        <variablelist>
4634          <varlistentry>
4635            <term>
4636              <methodsynopsis language="java">
4637                <modifier>public</modifier>
4638                <void/>
4639                <methodname>logChanges</methodname>
4640                <methodparam>
4641                  <type>LogManager</type>
4642                  <parameter>logManager</parameter>
4643                </methodparam>
4644                <methodparam>
4645                  <type>EntityDetails</type>
4646                  <parameter>details</parameter>
4647                </methodparam>
4648              </methodsynopsis>
4649            </term>
4650            <listitem>
4651              <para>
4652              This method is called whenever a change has been detected
4653              in an entity. The <varname>details</varname> variable contains
4654              information about the entity and, to a certain degree,
4655              what changes that has been made.
4656              </para>
4657            </listitem>
4658          </varlistentry>
4659        </variablelist>
4660      </sect3>
4661    </sect2>
4662  </sect1>
4663 
4664  <sect1 id="plugin_developer.signals">
4665    <title>Enable support for aborting a running a plug-in</title>
4666   
4667    <para>
4668      BASE includes a simple signalling system that can be used to send
4669      signals to plug-ins. The system was primarly developed to allow a user
4670      to kill a plug-in when it is executing. Therfore, the focus of this chapter
4671      will be how to implement a plug-in to make it possible to kill it
4672      during it's execution.
4673    </para>
4674   
4675    <para>
4676      Since we don't want to do this by brute force such as destroying the
4677      process or stopping thread the plug-in executes in, cooperation is needed
4678      by the plug-in. First, the plug-in must implement the
4679      <interfacename docapi="net.sf.basedb.core.signal">SignalTarget</interfacename>
4680      interface. From this, a <interfacename 
4681      docapi="net.sf.basedb.core.signal">SignalHandler</interfacename> can be created.
4682      A plug-in may choose to implement it's own signal handler or use an existing
4683      implementation. BASE, for example, provides the <classname docapi="net.sf.basedb.core.signal"
4684      >ThreadSignalHandler</classname> implementation that supports the <constant>ABORT</constant> signal.
4685      This is a simple implementation that just calls <code>Thread.interrupt()</code>
4686      on the plug-in worker thread. This may cause two different effects:
4687    </para>
4688   
4689    <itemizedlist>
4690    <listitem>
4691      <para>
4692      The <code>Thread.interrupted()</code> flag is set. The plug-in must check this
4693      at regular intervals and if the flag is set it must cleanup, rollback
4694      open transactions and exit as soon as possible.
4695      </para>
4696    </listitem>
4697   
4698    <listitem>
4699      <para>
4700      If the plug-in is waiting in a blocking call that is interruptable, for
4701      example <code>Thread.sleep()</code>, an <classname>InterruptedException</classname>
4702      is thrown. This should cause the same actions as if the flag was set to happen.
4703      </para>
4704     
4705      <warning>
4706        <title>Not all blocking calls are interruptable</title>
4707        <para>
4708        For example calling <code>InputStream.read()</code> may leave the
4709        plug-in waiting in a non-interruptable state. In this case there
4710        is nothing BASE can do to wake it up again.
4711        </para>
4712      </warning>
4713    </listitem>
4714    </itemizedlist>
4715   
4716    <para>
4717      Here is a general outline for a plug-in that uses the
4718      <classname>ThreadSignalHandler</classname>.
4719    </para>
4720    <programlisting language="java">
4721<![CDATA[
4722private ThreadSignalHandler signalHandler;
4723public SignalHandler getSignalHandler()
4724{
4725   signalHandler = new ThreadSignalHandler();
4726   return signalHandler;
4727}
4728
4729public void run(Request request, Response response, ProgressReporter progress)
4730{
4731   if (signalHandler != null) signalHandler.setWorkerThread(null);
4732   beginTransaction();
4733   boolean done = false;
4734   boolean interrupted = false;
4735   while (!done && !interrupted)
4736   {
4737      try
4738      {
4739         done = doSomeWork(); // NOTE! This must not take forever!
4740         interrupted = Thread.interrupted();
4741      }
4742      catch (InterruptedException ex)
4743      {
4744         // NOTE! Try-catch is only needed if thread calls
4745         // a blocking method that is interruptable
4746         interrupted = true;
4747      }
4748   }
4749   if (interrupted)
4750   {
4751      rollbackTransaction();
4752      response.setError("Aborted by user", null);
4753   }
4754   else
4755   {
4756      commitTransaction();
4757      response.setDone("Done");
4758   }
4759}
4760]]>
4761</programlisting>
4762   
4763    <para>
4764      Other signal handler implementations are
4765      <classname docapi="net.sf.basedb.core.signal">ProgressReporterSignalHandler</classname>
4766      and <classname docapi="net.sf.basedb.core.signal">EnhancedThreadSignalHandler</classname>.
4767      The latter handler also has support for the <constant>SHUTDOWN</constant> signal
4768      which is sent to plug-in when the system is shutting down. Clever plug-ins may use
4769      this to enable them to be restarted when the system is up and running again.
4770      See that javadoc for information about how to use it. For more information about the
4771      signalling system as a whole, see <xref linkend="core_api.signals" />.
4772    </para>
4773   
4774  </sect1>
4775 
4776  <sect1 id="plugin_developer.classload">
4777    <title>How BASE load plug-in classes</title>
4778   
4779    <para>
4780      We recommend that plug-in JAR files are installed outside the web server's
4781      classpath. If you are using Tomcat this means that you should not
4782      install the plug-in in the <filename>&lt;base-dir&gt;/www/WEB-INF/lib</filename>
4783      directory or any other directory where the web server keeps it's classes.
4784      The rest of the information in this section only applies to plug-ins that
4785      have been installed following this restriction.
4786    </para>
4787   
4788    <para>
4789      If the above recommendation has been followed BASE will use it's
4790      own classloader to load the plug-in classes. This have several
4791      benefits:
4792    </para>
4793   
4794    <itemizedlist>
4795    <listitem>
4796      <para>
4797      New plug-ins can be installed and existing plug-ins can be updated
4798      without restarting the web server. If the <property>plugins.autounload</property>
4799      setting in <filename>base.config</filename> has been enabled all you have to
4800      do to update a plug-in is to replace the JAR file with a new version.
4801      BASE will automatically load the new classes the next time the plug-in
4802      is used. If the option isn't enabled, the server admin has to manually
4803      unload the old code from the web interface first.
4804      </para>
4805    </listitem>
4806   
4807    <listitem>
4808      <para>
4809      Plug-ins may use it's own 3-rd party libraries without interfering
4810      with other plug-ins. This may be important because a plug-in may depend on
4811      a certain version of a library while another plug-in may depend on
4812      a different version. Since BASE is using different class-loaders
4813      for different plug-ins this is not a problem.
4814      </para>
4815    </listitem>
4816     
4817    </itemizedlist>
4818   
4819    <para>
4820      The classloading scheme used by BASE also means plug-in
4821      developers must pay attention to a few things:
4822    </para>
4823   
4824    <itemizedlist>
4825    <listitem>
4826      <para>
4827      A plug-in can only access/use classes from it's own JAR file, BASE core
4828      classes, Java system classes and from JAR files listed in the plug-in's
4829      <filename>MANIFEST.MF</filename> file. See <xref linkend="plugin_developer.organize" />.
4830      </para>
4831    </listitem>
4832   
4833    <listitem>
4834      <para>
4835      A plug-in can also access other plug-ins, but only via the methods and
4836      interfaces defined in BASE. In the following example we assume that
4837      there are two plug-ins, <classname>ex.MyPlugin</classname> 
4838      and <classname>ex.MyOtherPlugin</classname>,
4839      located in two different JAR files. The code below is executing
4840      in the <classname>ex.MyPlugin</classname>:
4841      </para>
4842     
4843      <programlisting language="java">
4844// Prepare to load MyOtherPlugin
4845SessionControl sc = ...
4846DbControl dc = ...
4847PluginDefinition def = PluginDefinition.getByClassName(dc, "ex.MyOtherPlugin");
4848
4849// Ok
4850Plugin other = def.newInstance(Plugin.class, null, sc, null, null);
4851
4852// Not ok; fails with ClassCastException
4853MyOtherPlugin other = def.newInstance(MyOtherPlugin.class, null, sc, null, null);
4854</programlisting>
4855     
4856      <para>
4857      The reason that the second call fails is that BASE uses a different
4858      classloader to load the <classname>ex.MyOtherPlugin</classname> class. This
4859      class is not (in Java terms) the same as the <classname>ex.MyOtherPlugin</classname> 
4860      class loaded by the classloader that loaded the <classname>ex.MyPlugin</classname>
4861      class. If, on the other hand, both plug-ins are located in the same
4862      JAR file BASE uses the same classloader and the second call will succeed.
4863      </para>
4864     
4865      <para>
4866      The first call always succeeds because it uses the <interface>Plugin</interface>
4867      interface which is defined by BASE. This class is loaded by the web servers class
4868      loader and is the same for all plug-ins.
4869      </para>
4870     
4871      <para>
4872      A third option is that the <classname>ex.MyPlugin</classname>
4873      lists the JAR file where <classname>ex.MyOtherPlugin</classname> is
4874      located in it's <filename>MANIFEST.MF</filename> file. Then, the following
4875      code can be used: <code>MyOtherPlugin other = new MyOtherPlugin();</code>
4876      </para>
4877     
4878    </listitem>
4879    </itemizedlist>
4880   
4881    <para>
4882      Tomcat includes a good document describing how classloading is implemented
4883      in Tomcat: <ulink url="http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html"
4884        >http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html</ulink>.
4885     
4886      BASE's classloading scheme isn't as complex as Tomcat's, but it
4887      very similar to how Tomcat loads different web applications.
4888      The figure on the linked document could be extended with another level
4889      with separate classloaders for each plug-in as child
4890      classloaders to the web application classloaders. 
4891    </para>
4892   
4893    <para>
4894      As of BASE 2.13 the default search order for classes has been changed. The
4895      default is now to first look in the plug-ins class path (eg. in the same
4896      JAR file and in files listed in the <filename>MANIFEST.MF</filename> file).
4897      Only if the class is not found the search is delegated to the parent class
4898      loader. This behaviour can be changed by setting <code>X-Delegate-First: true</code>
4899      in the <filename>MANIFEST.MF</filename> file. If this property is set the
4900      parent class loader is search first. This is the same as in BASE 2.12 and
4901      earlier.
4902    </para>
4903   
4904    <note>
4905      The benefit with the new search order is that plug-ins may use
4906      a specific version of any external package even if the same
4907      package is part of the BASE distribution. This was not possible before
4908      since the package in the BASE distribution was loaded first.
4909    </note>
4910  </sect1>
4911 
4912  <sect1 id="plugin_developer.example">
4913    <title>Example plug-ins (with download)</title>
4914    <para>
4915      We have created some example plug-ins which demonstrates how to
4916      use the plug-in system and how to create an interactive plug-in that
4917      can ask a user for one or more parameters.
4918   
4919      You can <ulink url="http://baseplugins.thep.lu.se/wiki/net.sf.basedb.examples.plugins">download
4920      a tar file with the source and compiled plug-in code</ulink> from the BASE plug-ins website.
4921    </para>
4922   
4923  </sect1>
4924</chapter>
Note: See TracBrowser for help on using the repository browser.