source: trunk/doc/src/docbook/developer/plugins.xml @ 7548

Last change on this file since 7548 was 7548, checked in by Nicklas Nordborg, 4 years ago

Fixes #2134: Deprecate secondary storage

Code and documentatation has been updated.

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