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

Last change on this file since 3479 was 3479, checked in by Peter Johansson, 15 years ago

fixed some typos and grammar

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 89.4 KB
Line 
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE chapter PUBLIC
3    "-//Dawid Weiss//DTD DocBook V3.1-Based Extension for XML and graphics inclusion//EN"
4    "../../../../lib/docbook/preprocess/dweiss-docbook-extensions.dtd">
5<!--
6  $Id: plugin_developer.xml 3479 2007-06-12 15:23:39Z peter $:
7 
8  Copyright (C) Authors contributing to this file.
9 
10  This file is part of BASE - BioArray Software Environment.
11  Available at http://base.thep.lu.se/
12 
13  BASE is free software; you can redistribute it and/or
14  modify it under the terms of the GNU General Public License
15  as published by the Free Software Foundation; either version 2
16  of the License, or (at your option) any later version.
17 
18  BASE is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  GNU General Public License for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with this program; if not, write to the Free Software
25  Foundation, Inc., 59 Temple Place - Suite 330,
26  Boston, MA  02111-1307, USA.
27-->
28
29<chapter id="plugin_developer">
30  <?dbhtml dir="plugin_developer"?>
31  <title>Plug-in developer</title>
32  <sect1 id="plugin_developer.organize">
33    <title>How to organize your plug-in project</title>
34   
35    <sect2 id="plugin_developer.organize.ant">
36      <title>Using Ant</title>
37      <para>
38        Here is a simple example of how you might organize your project using ant
39        (<ulink url="http://ant.apache.org">http://ant.apache.org</ulink>) as the build
40        tool. This is just a recommendation that we have found to be working
41        well. You may choose to do it another way.
42      </para>
43
44      <sect3 id="plugin_developer.organize.ant.directories">
45        <title>Directory layout</title>
46        <para>
47       
48          Create a directory on your computer where you want
49          to store your plug-in project. This directory is the
50          <filename class="directory"><replaceable>pluginname</replaceable>/</filename>
51          directory in the listing below. You should also create some subdirectories:
52         
53          <literallayout>
54<filename class="directory"><replaceable>pluginname</replaceable>/</filename>
55<filename class="directory"><replaceable>pluginname</replaceable>/bin/</filename>
56<filename class="directory"><replaceable>pluginname</replaceable>/lib/</filename>
57<filename class="directory"><replaceable>pluginname</replaceable>/src/<replaceable>org/company</replaceable>/</filename>
58          </literallayout>
59          The
60          <filename class="directory">bin/</filename>
61          directory is empty to start with. It will contain the compiled code.
62          In the <filename class="directory">lib/</filename>
63          directory you should put <filename>BASE2Core.jar</filename>
64          and other library files your plug-in depends on. The
65          <filename class="directory">src/</filename>
66          directory contains your source code. In this directory you should create
67          subdirectories corresponding to the package name of your plug-in
68          class(es). See <ulink url="http://en.wikipedia.org/wiki/Java_package"
69          >http://en.wikipedia.org/wiki/Java_package</ulink> for information
70          about conventions for naming packages.
71        </para>
72      </sect3>
73   
74      <sect3 id="plugin_developer.organize.ant.buildfile">
75        <title>The build file</title>
76        <para>
77          In the root of your directory, create the build file:
78          <filename>build.xml</filename>
79          . Here is an example that will compile your plug-in and put it in a JAR file.
80        </para>
81        <example id="plugin_developer.organize.build.file">
82          <title>A simple build file</title>
83<programlisting>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
84&lt;project
85   name="MyPlugin"
86   default="build.plugin"
87   basedir="."
88   &gt;
89
90   &lt;!-- variables used --&gt;
91   &lt;property name="plugin.name" value="MyPlugin" /&gt;
92   &lt;property name="src" value="src" /&gt;
93   &lt;property name="bin" value="bin" /&gt;
94
95   &lt;!-- set up classpath for compiling --&gt;
96   &lt;path id="classpath"&gt;
97      &lt;fileset dir="lib"&gt;
98         &lt;include name="**/*.jar"/&gt;
99      &lt;/fileset&gt;
100   &lt;/path&gt;
101
102   
103   &lt;!-- main target --&gt;
104   &lt;target
105      name="build.plugin" 
106      description="Compiles the plug-in and put in jar"
107      &gt;
108      &lt;javac
109         encoding="ISO-8859-1"
110         srcdir="${src}"
111         destdir="${bin}"
112         classpathref="classpath"&gt;
113      &lt;/javac&gt;
114      &lt;jar
115         jarfile="${plugin.name}.jar"
116         basedir="bin"
117         manifest="MANIFEST.MF"
118         &gt;
119      &lt;/jar&gt;
120
121    &lt;/target&gt;
122&lt;/project&gt; </programlisting>
123        </example>
124        <para>
125          If your plug-in depends on other JAR files than the
126          <filename>BASE2Core.jar</filename> you must create a file called
127          <filename>MANIFEST.MF</filename> in the project root directory.
128          List the other JAR files as in the following example.
129          If your plug-in doesn't depend on other JAR files, remove the
130          <sgmltag class="attribute">manifest</sgmltag> 
131          attribute of the <sgmltag class="starttag">jar</sgmltag> tag.
132<programlisting>Manifest-Version: 1.0
133Class-Path: OtherJar.jar ASecondJar.jar</programlisting>
134        </para>
135      </sect3>
136       
137      <sect3 id="plugin_developer.organize.ant.build">
138        <title>Building the plug-in</title>
139        <para>
140          Compile the plug-in simply by typing
141          <command>ant</command>
142          in the console window. If all went well the
143          <filename>MyPlugin.jar</filename>
144          will be created in the same directory.
145        </para>
146        <para>
147          To install the plug-in copy the JAR file to the server including the dependent JAR
148          files (if any). Place all files together in the same directory. For more
149          information read
150          <xref linkend="plugins.installation" />
151        </para>
152      </sect3>
153    </sect2>
154   
155    <sect2 id="plugin_developer.organize.eclipse">
156      <title>With Eclipse</title>
157      <para>TODO</para>
158    </sect2>
159   
160  </sect1>
161
162  <sect1 id="plugin_developer.api">
163    <title>The Plug-in API</title>
164    <para>
165    </para>
166   
167    <sect2 id="plugin_developer.api.interfaces">
168   
169      <title>The main plug-in interfaces</title>
170      <para>
171        The Base2 core defines two interfaces and one
172        abstract class that are vital for implementing plug-ins:
173        <itemizedlist spacing="compact">
174          <listitem>
175            <simpara>
176              <interfacename>net.sf.basedb.core.plugin.Plugin</interfacename>
177            </simpara>
178          </listitem>
179          <listitem>
180            <simpara>
181              <interfacename>net.sf.basedb.core.plugin.InteractivePlugin</interfacename>
182            </simpara>
183          </listitem>
184          <listitem>
185            <simpara>
186              <classname>net.sf.basedb.core.plugin.AbstractPlugin</classname>
187            </simpara>
188          </listitem>
189        </itemizedlist>
190       
191        A plug-in is always required to implement the
192        <interfacename>Plugin</interfacename> interface.
193        The <interfacename>InteractivePlugin</interfacename>
194        interface is optional, and is only needed if you want user interaction.
195        The <classname>AbstractPlugin</classname> is a useful base class
196        that your plug-in can use as a superclass. It provides default implementations
197        for some of the interface methods and also has utility methods for
198        validating and storing job and configuration parameter values. Another
199        reason to use this class as a superclass is that it will shield your
200        plug-in from future changes to the Plug-in API. For example, if
201        we decide that a new method is needed in the <interfacename>Plugin</interfacename> 
202        interface we will also try to add a default implementation in
203        the <classname>AbstractPlugin</classname> class.
204      </para>
205 
206      <important>
207        <para>
208        A plug-in must also have public no-argument contructor. Otherwise, BASE will not
209        be able to create instances of the class.
210        </para>
211      </important>
212     
213      <sect3 id="plugin_developer.api.interfaces.plugin">
214        <title>net.sf.basedb.core.plugin.Plugin</title>
215        <para>This interface defines seven methods and must be implemented by all plug-ins.</para>
216        <variablelist>
217          <varlistentry>
218            <term>
219              <methodsynopsis language="java">
220                <modifier>public</modifier>
221                <type>About</type>
222                <methodname>getAbout</methodname>
223                <void />
224              </methodsynopsis>
225            </term>
226            <listitem>
227              <para>
228                Return information about the plug-in, i.e. the name, version, and a short
229                description about what the plug-in does. The
230                <classname>About</classname>
231                object also has fields for naming the author and various other contact
232                information. The returned information is copied by the core at
233                installation time into the database. The only required information is
234                the name of the plug-in. All other fields may have null values.
235              </para>
236              <example id="net.sf.basedb.core.plugin.Plugin.getAbout">
237                <title>A typical implementation stores this information in a static field</title>
238<programlisting>private static final About about = new AboutImpl
239(
240   "Spot images creator",
241   "Converts a full-size scanned image into smaller preview jpg " +
242   "images for each individual spot.",
243   "2.0",
244   "2006, Department of Theoretical Physics, Lund University",
245   null,
246   "base@thep.lu.se",
247   "http://base.thep.lu.se"
248);
249 
250public About getAbout()
251{
252   return about;
253}</programlisting>
254              </example>
255            </listitem>
256          </varlistentry>
257          <varlistentry>
258            <term>
259              <methodsynopsis language="java">
260                <modifier>public</modifier>
261                <type>Plugin.MainType</type>
262                <methodname>getMainType</methodname>
263                <void />
264              </methodsynopsis>
265            </term>
266            <listitem>
267              <para>
268                Return information about the main type of plug-in. The
269                <classname>MainType</classname>
270                is an enumeration with five possible values:
271                <itemizedlist>
272                  <listitem>
273                    <para>
274                      <constant>ANALYZE</constant>:
275                      An analysis plug-in
276                    </para>
277                  </listitem>
278                  <listitem>
279                    <para>
280                      <constant>EXPORT</constant>:
281                      A plug-in that exports data
282                    </para>
283                  </listitem>
284                  <listitem>
285                    <para>
286                      <constant>IMPORT</constant>:
287                      A plug-in that imports data
288                    </para>
289                  </listitem>
290                  <listitem>
291                    <para>
292                      <constant>INTENSITY</constant>:
293                      A plug-in that calculates the original spot intensities
294                      from raw data
295                    </para>
296                  </listitem>
297                  <listitem>
298                    <para>
299                      <constant>OTHER</constant>:
300                      Any other type of plug-in
301                    </para>
302                  </listitem>
303                </itemizedlist>
304                The returned value is stored in the database but is otherwise not used
305                by the core. Client applications (such as the web client) will probably
306                use this information to group the plug-ins, i.e., a button labeled
307                &gbExport;
308                will let you select among the export plug-ins.
309              </para>
310              <example id="net.sf.basedb.core.plugin.Plugin.getMainType">
311                <title>A typical implementation just return one of the values</title>
312<programlisting>public Plugin.MainType getMainType()
313{
314   return Plugin.MainType.OTHER;
315}</programlisting>
316              </example>
317            </listitem>
318          </varlistentry>
319          <varlistentry>
320            <term>
321              <methodsynopsis language="java">
322                <modifier>public</modifier>
323                <type>boolean</type>
324                <methodname>supportsConfigurations</methodname>
325                <void />
326              </methodsynopsis>
327            </term>
328            <listitem>
329              <para>
330                If this method returns true, the plug-in can have different
331                configurations, (i.e.
332                <classname>PluginConfiguration</classname>).
333                Note that this method may return true even if the
334                <interfacename>InteractivePlugin</interfacename>
335                interface isn't implemented. The
336                <classname>AbstractPlugin</classname>
337                returns true for this method, which is the old way before the
338                introduction of this method.
339              </para>
340            </listitem>
341          </varlistentry>
342          <varlistentry>
343            <term>
344              <methodsynopsis language="java">
345                <modifier>public</modifier>
346                <type>boolean</type>
347                <methodname>requiresConfiguration</methodname>
348                <void />
349              </methodsynopsis>
350            </term>
351            <listitem>
352              <para>
353                If this method returns true, the plug-in must have a configuration
354                to be able to run. For example, some of the core import plug-ins
355                must have information about the file format, to be able to import
356                any data.
357                The
358                <classname>AbstractPlugin</classname>
359                returns false for this method, which is the old way before the
360                introduction of this method.
361              </para>
362            </listitem>
363          </varlistentry>
364          <varlistentry>
365            <term>
366              <methodsynopsis language="java">
367                <modifier>public</modifier>
368                <void />
369                <methodname>init</methodname>
370                <methodparam>
371                  <type>SessionControl</type>
372                  <parameter>sc</parameter>
373                </methodparam>
374                <methodparam>
375                  <type>ParameterValues</type>
376                  <parameter>configuration</parameter>
377                </methodparam>
378                <methodparam>
379                  <type>ParameterValues</type>
380                  <parameter>job</parameter>
381                </methodparam>
382                <exceptionname>BaseException</exceptionname>
383              </methodsynopsis>
384            </term>
385            <listitem>
386              <para>
387                Prepare the plug-in for execution (or configuration). If the plug-in needs
388                to do some initialization this is the place to do it. A typical
389                implementation however only stores the passed parameters in instance
390                variables for later use.
391              </para>
392              <para>
393                The parameters passed to this method has vital information that is
394                needed to execute the plug-in. The
395                <classname>SessionControl</classname>
396                is a central core object holding information about the logged in
397                user and is used to create
398                <classname>DbControl</classname>
399                objects which allows a plug-in to connect to the database to read, add or
400                update information. The two
401                <classname>ParameterValues</classname>
402                objects contain information about the configuration and job
403                parameters to the plug-in.
404                The <varname>configuration</varname> object holds all parameters stored
405                together with a <classname>PluginConfiguration</classname>
406                object in the database. If the plug-in is started without
407                a configuration this object is null.
408                The <varname>job</varname> object holds all parameters that are
409                stored together with a <classname>Jon</classname> object in the
410                database. This object is null if the plug-in is started without a job.
411              </para>
412              <para>
413                The difference between a configuration parameter and a job parameter is
414                that a configuration is usually something an administrator sets up,
415                while a job is an actual execution of a plug-in. For example, a
416                configuration for an import plug-in holds the regular expressions needed
417                to parse a text file and find the headers, sections and data lines,
418                while the job holds the file to parse.
419              </para>
420              <para>
421                The
422                <classname>AbstractPlugin</classname>
423                contains an implementation of this method that saves the passed objects
424                in protected instance variables. If you override this method
425                we recommend that you also call <code>super.init()</code>.
426              </para>
427              <example id="net.sf.basedb.core.plugin.Plugin.init">
428                <title>The <classname>AbstractPlugin</classname> implementation of this Plugin.init()</title>
429<programlisting>protected SessionControl sc = null;
430protected ParameterValues configuration = null;
431protected ParameterValues job = null;
432/**
433   Store copies of the session control, plug-in and job configuration. These
434   are available to subclasses in the {@link #sc}, {@link #configuration}
435   and {@link #job} variables. If a subclass overrides this method it is
436   recommended that it also calls super.init(sc, configuration, job).
437*/
438public void init(SessionControl sc,
439   ParameterValues configuration, ParameterValues job)
440   throws BaseException
441{
442   this.sc = sc;
443   this.configuration = configuration;
444   this.job = job;
445}</programlisting>
446              </example>
447            </listitem>
448          </varlistentry>
449          <varlistentry>
450            <term>
451              <methodsynopsis language="java">
452                <modifier>public</modifier>
453                <void />
454                <methodname>run</methodname>
455                <methodparam>
456                  <type>Request</type>
457                  <parameter>request</parameter>
458                </methodparam>
459                <methodparam>
460                  <type>Response</type>
461                  <parameter>response</parameter>
462                </methodparam>
463                <methodparam>
464                  <type>ProgressReporter</type>
465                  <parameter>progress</parameter>
466                </methodparam>
467              </methodsynopsis>
468            </term>
469            <listitem>
470              <para>
471                Runs the plug-in. The <varname>request</varname>
472                parameter is of historical interest only. It has no useful information
473                and can be ignored.
474              </para>
475              <para>
476                The <varname>progress</varname> parameter
477                can be used by a plug-in to report it's progress back to the core. The
478                core will usually send the progress information to the database, which
479                allows users to see exactly how the plug-in is progressing from the web
480                interface. This parameter can be null, but if it isn't we recommend all
481                plug-ins to use it. However, it should be used sparingly, since each call
482                to set the progress results in a database update. If the execution
483                involves several thousands of items it is a bad idea to update the
484                progress after processing each one of them. A good starting point is to
485                divide the work into 100 pieces each representing 1% of the work, i.e.,
486                if the plug-in should export 100 000 items it should report progress
487                after every 1000 items.
488              </para>
489              <para>
490                The <varname>response</varname>
491                parameter is used to tell the core if the plug-in was successful or
492                failed. Not setting a response is considered a failure by the core. From
493                the run method it is only allowed to use the
494                <methodname>setDone()</methodname>
495                or the
496                <methodname>setError()</methodname>
497                methods.
498              </para>
499             
500              <note>
501                It is also considered bad practice to let exceptions escape
502                out from this method. Always use <code>try...catch</code>
503                to catch exceptions and use <code>response.setError()</code>
504                to reporter the error back to the core.
505              </note>
506             
507              <example id="net.sf.basedb.core.plugin.Plugin.run">
508                <title>
509                  Here is a skeleton that we recommend each plug-in to use in it's
510                  implementation of the
511                  <methodname>run()</methodname>
512                  method
513                </title>
514<programlisting>public void run(Request request, Response response, ProgressReporter progress)
515{
516   // Open a connection to the database
517   // sc is set by init() method
518   DbControl dc = sc.newDbControl();
519   try
520   {
521      // Insert code for plug-in here
522
523      // Commit the work
524      dc.commit();
525      response.setDone("Plug-in ended successfully");
526   }
527   catch (Throwable t)
528   {
529      // All exceptions must be catched and sent back
530      // using the response object
531      response.setError(t.getMessage(), Arrays.asList(t));
532   }
533   finally
534   {
535      // IMPORTANT!!! Make sure opened connections are closed
536      if (dc != null) dc.close();
537   }
538}</programlisting>
539              </example>
540            </listitem>
541          </varlistentry>
542          <varlistentry>
543            <term>
544              <methodsynopsis language="java">
545                <modifier>public</modifier>
546                <void />
547                <methodname>done</methodname>
548                <void />
549              </methodsynopsis>
550            </term>
551            <listitem>
552              <para>
553                Clean up all resources after executing the plug-in. This method mustn't
554                throw any exceptions.
555              </para>
556              <example id="net.sf.basedb.core.plugin.Plugin.done">
557                <title>
558                  The
559                  <classname>AbstractPlugin</classname>
560                  contains an implementation of the <methodname>done()</methodname>
561                  method simply sets the
562                  parameters passed to the
563                  <methodname>init()</methodname>
564                  method to null
565                </title>
566<programlisting>/**
567   Clears the variables set by the init method. If a subclass
568   overrides this method it is recommended that it also calls super.done().
569*/
570public void done()
571{
572   configuration = null;
573   job = null;
574   sc = null;
575}</programlisting>
576              </example>
577            </listitem>
578          </varlistentry>
579        </variablelist>
580      </sect3>
581 
582      <sect3 id="plugin_developer.api.interfaces.interactive">
583        <title>net.sf.basedb.core.plugin.InteractivePlugin</title>
584        <para>
585          If you want the plug-in to be able to interact with the user you must also implement
586          this interface. This is probably the case for most plug-ins. Among the core plug-ins
587          shipped with BASE the
588          <classname>SpotImageCreator</classname>
589          is one plug-in that doesn't interact with the user. Instead, the web client has
590          special JSP pages that handles all the interaction, creates a job for it and sets
591          the parameters. This, kind of hardcoded, approach can be used for other plug-ins as
592          well, but then it usually requires modification of the client application as well.
593        </para>
594        <para>
595          The
596          <interfacename>InteractivePlugin</interfacename>
597          has three main tasks:
598         
599          <orderedlist>
600          <listitem>
601            <para>
602            Tell a client application where the plug-in should be plugged
603            in.
604            </para>
605          </listitem>
606          <listitem>
607            <para>
608            Ask the users for configuration and job parameters.
609            </para>
610          </listitem>
611         
612          <listitem>
613            <para>
614            Validate parameter values entered by the user and store those in the
615            database.
616            </para>
617          </listitem>
618          </orderedlist>
619          This requires that the following methods are
620          implemented.
621        </para>
622       
623        <variablelist>
624          <varlistentry>
625            <term>
626              <methodsynopsis language="java">
627                <modifier>public</modifier>
628                <type>Set&lt;GuiContext&gt;</type>
629                <methodname>getGuiContexts</methodname>
630                <void />
631              </methodsynopsis>
632            </term>
633            <listitem>
634              <para>
635                Return information about where the plug-in should be plugged in. Each
636                place is identified by a
637                <classname>GuiContext</classname>
638                object, which is an
639                <classname>Item</classname>
640                and a
641                <classname>Type</classname>.
642                The item is one of the objects defined by the
643                <classname>net.sf.basedb.core.Item</classname>
644                enumeration and the type is either
645                <constant>Type.LIST</constant>
646                or
647                <constant>Type.ITEM</constant>, which corresponde to the
648                list view and the single-item view in the web client.
649              </para>
650              <para>
651                For example, the
652                <varname>GuiContext</varname> =
653                (<constant>Item.REPORTER</constant>,
654                <constant>Type.LIST</constant>)
655                tells a client application that this plug-in can be plugged in whenever a
656                list of reporters is displayed. The
657                <varname>GuiContext</varname> =
658                (<constant>Item.REPORTER</constant>,
659                <constant>Type.ITEM</constant>)
660                tells a client application that this plug-in can be plugged in whenever
661                a single reporter is displayed. The first case may be appropriate for a
662                plug-in that imports or exports reporters. The second case may be used by
663                a plug-in that updates the reporter information from an external source
664                (well, it may make sense to use this in the list case as well).
665              </para>
666              <para>
667                The returned information is copied by the core at installation time to
668                make it easy to ask for all plug-ins for a certain
669                <classname>GuiContext</classname>.
670              </para>
671              <para>
672                A typical implementation creates a static unmodifiable
673                <classname>Set</classname>
674                which is returned by this method. It is important that the returned set
675                can't be modified. It may be a security issue if a misbehaving
676                client application does that.
677              </para>
678              <example id="net.sf.basedb.core.plugin.InteractivePlugin.getGuiContexts">
679                <title>
680                  A typical implementation of
681                  <methodname>getGuiContexts</methodname>
682                </title>
683<programlisting>// From the net.sf.basedb.plugins.RawDataFlatFileImporter plug-in
684private static final Set&lt;GuiContext&gt; guiContexts =
685   Collections.singleton(new GuiContext(Item.RAWBIOASSAY, GuiContext.Type.ITEM));
686
687public Set&lt;GuiContext&gt; <methodname>getGuiContexts</methodname>()
688{
689   return <returnvalue>guiContexts</returnvalue>;
690}</programlisting>
691              </example>
692            </listitem>
693          </varlistentry>
694          <varlistentry>
695            <term>
696              <methodsynopsis language="java">
697                <modifier>public</modifier>
698                <type>String</type>
699                <methodname>isInContext</methodname>
700                <methodparam>
701                  <type>GuiContext</type>
702                  <parameter>context</parameter>
703                </methodparam>
704                <methodparam>
705                  <type>Object</type>
706                  <parameter>item</parameter>
707                </methodparam>
708              </methodsynopsis>
709            </term>
710            <listitem>
711              <para>
712                This method is called to check if a particular item is usable for the
713                plug-in. This method is only invoked from the single-item view.
714                Thus, <code>context.getType()</code> always returns
715                <constant>Type.ITEM</constant>, and <code>context.getItem()</code>
716                returns a value corresponding to the type of item that is passed
717                in the <varname>item</varname> parameter. Here is an example:
718               
719                <informalexample>
720                <para>
721                The user has selected a specific sample and the client
722                application is now displaying information about that sample.
723                Thus, our
724                <varname>GuiContext</varname> =
725                (<constant>Item.SAMPLE</constant>,
726                <constant>Type.ITEM</constant>).
727               
728                Now, the client application asks for a list of plug-ins supporting
729                this context and for each one in the list calls this method with the
730                current sample as the value of the <varname>item</varname> parameter.
731                </para>
732                </informalexample>
733
734                If the plug-in can do whatever it is supposed to do it should
735                return null, otherwise it should return a string with a message
736                explaining why it can't.
737              </para>
738              <para>
739                Here is a real example from the
740                <classname>RawDataFlatFileImporter</classname>
741                plug-in which imports raw data to a
742                <classname>RawBioAssay</classname>.
743               
744                Thus,
745                <varname>GuiContext</varname> =
746                (<constant>Item.RAWBIOASSAY</constant>,
747                <constant>Type.ITEM</constant>),
748               
749                but the plug-in can only import data if there is no data already, and
750                if the raw bioassay has the same raw data type as the plug-in has been
751                configured for.
752              </para>
753              <example id="net.sf.basedb.core.plugin.InteractivePlugin.isInContext">
754                <title>
755                  A simple implementation of
756                  <methodname>isInContext</methodname>
757                </title>
758<programlisting>/**
759   Returns null if the item is a {@link RawBioAssay} of the correct
760   {@link RawDataType} and doesn't already have spots.
761*/
762public String isInContext(GuiContext context, Object item)
763{
764   String message = null;
765   if (item == null)
766   {
767      message = "The object is null";
768   }
769   else if (!(item instanceof RawBioAssay))
770   {
771      message = "The object is not a RawBioAssay: " + item;
772   }
773   else
774   {
775      RawBioAssay rba = (RawBioAssay)item;
776      String rawDataType = (String)configuration.getValue("rawDataType");
777      if (rba.getSpots() > 0)
778      {
779         message = "The raw bioassay already has spots: " + rba.getName();
780      }
781      else if (!rba.getRawDataType().getId().equals(rawDataType))
782      {
783         message = "Unsupported raw data type: " + rba.getRawDataType().getName();
784      }
785   }
786   return message;   
787}</programlisting>
788              </example>
789            </listitem>
790          </varlistentry>
791          <varlistentry>
792            <term>
793              <methodsynopsis language="java">
794                <modifier>public</modifier>
795                <type>RequestInformation</type>
796                <methodname>getRequestInformation</methodname>
797                <methodparam>
798                  <type>GuiContext</type>
799                  <parameter>context</parameter>
800                </methodparam>
801                <methodparam>
802                  <type>String</type>
803                  <parameter>command</parameter>
804                </methodparam>
805                <exceptionname>BaseException</exceptionname>
806              </methodsynopsis>
807            </term>
808            <listitem>
809              <para>
810                Ask the plug-in for parameters that needs to be entered by the user. The
811                <classname>GuiContext</classname>
812                parameter is one of the contexts returned by the
813                <methodname>getGuiContexts</methodname>
814                method. The command is a string telling the plug-in what command was
815                executed. There are two predefined commands but as you will see the
816                plug-in may define it's own commands. The two predefined commands are
817                defined in the
818                <classname>net.sf.basedb.core.plugin.Request</classname>
819                class.
820                <variablelist>
821                  <varlistentry>
822                    <term>
823                      <constant>Request.COMMAND_CONFIGURE_PLUGIN</constant>
824                    </term>
825                    <listitem>
826                      <para>
827                        Used when an administrator is initiating a configuration
828                        of the plug-in.
829                      </para>
830                    </listitem>
831                  </varlistentry>
832                  <varlistentry>
833                    <term>
834                      <constant>Request.COMMAND_CONFIGURE_JOB</constant>
835                    </term>
836                    <listitem>
837                      <para>
838                        Used when a user has selected the plug-in for running a
839                        job.
840                      </para>
841                    </listitem>
842                  </varlistentry>
843                </variablelist>
844                Given this information the plug-in must return a
845                <classname>RequestInformation</classname>
846                object. This is simply a title, a description, and a list of parameters.
847                Usually the title will end up as the input form title and the
848                description as a help text for the entire form. Do not put information
849                about the individual parameters in this description, since each
850                parameter has a description of its own.
851              </para>
852              <example id="net.sf.basedb.core.plugin.InteractivePlugin.getRequestInformation">
853                <title>
854                  When running an import plug-in it needs to ask for the file to import
855                  from and if existing items should be updated or not
856                </title>
857<programlisting >// The complete request information
858private RequestInformation configure Job;
859
860// The parameter that asks for a file to import from
861private PluginParameter&lt;File&gt; file Parameter;
862
863// The parameter that asks if existing items should be updated or not
864private PluginParameter&lt;Boolean&gt; updateExistingParameter;
865
866public RequestInformation getRequestInformation(GuiContext context, String command)
867   throws BaseException
868{
869   RequestInformation requestInformation = null;
870   if (command.equals(Request.COMMAND_CONFIGURE_PLUGIN))
871   {
872      requestInformation = getConfigurePlugin();
873   }
874   else if (command.equals(Request.COMMAND_CONFIGURE_JOB))
875   {
876      requestInformation = getConfigureJob();
877   }
878   return requestInformation;
879}
880
881/**
882   Get (and build) the request information for starting a job.
883*/
884private RequestInformation getConfigureJob()
885{
886   if (configureJob == null)
887   {
888      fileParameter = new PluginParameter&lt;File&gt;(
889         "file",
890         "File",
891         "The file to import the data from",
892         new FileParameterType(null, true, 1)
893      );
894     
895      updateExistingParameter = new PluginParameter&lt;Boolean&gt;(
896         "updateExisting",
897         "Update existing items",
898         "If this option is selected, already existing items will be updated " +
899         " with the information in the file. If this option isn't selected " +
900         " existing items are left untouched.",
901         new BooleanParameterType(false, true)
902      );
903
904      List&lt;PluginParameter&lt;?&gt;&gt; parameters =
905         new ArrayList&lt;PluginParameter&lt;?&gt;&gt;(2);
906      parameters.add(fileParameter);
907      parameters.add(updateExistingParameter);
908     
909      configureJob = new RequestInformation
910      (
911         Request.COMMAND_CONFIGURE_JOB,
912         "Select a file to import items from",
913         "Description",
914         parameters
915      );
916   }
917   return configureJob;
918}</programlisting>
919              </example>
920              <para>
921                As you can see it takes a lot of code to put together a
922                <classname>RequestInformation</classname>
923                object. For each parameter you need one
924                <classname>PluginParameter</classname>
925                object and one
926                <classname>ParameterType</classname>
927                object. To make life a little easier, a
928                <classname>ParameterType</classname>
929                can be reused for more than one
930                <classname>PluginParameter</classname>.
931              </para>
932             
933<programlisting>StringParameterType stringPT = new StringParameterType(255, null, true);
934PluginParameter one = new PluginParameter("one", "One", "First string", stringPT);
935PluginParameter two = new PluginParameter("two", "Two", "Second string", stringPT);
936// ... and so on</programlisting>
937              <para>
938                The
939                <classname>ParameterType</classname>
940                is an abstract base class for several subclasses each implementing a
941                specific type of parameter. The list of subclasses may grow in the
942                future, but here are the most important ones currently implemented.
943              </para>
944              <note>
945                <para>
946                  Most parameter types include support for supplying a predefined list
947                  of options to select from. In that case the list will be displayed
948                  as a drop-down list for the user, otherwise a free input field is
949                  used.
950                </para>
951              </note>
952              <variablelist>
953                <varlistentry>
954                  <term>
955                    <classname>StringParameterType</classname>
956                  </term>
957                  <listitem>
958                    <para>
959                      Asks for a string value. Includes an option for
960                      specifying the maximum length of the string.
961                    </para>
962                  </listitem>
963                </varlistentry>
964                <varlistentry>
965                  <term>
966                    <classname>FloatParameterType</classname>,
967                    <classname>DoubleParameterType</classname>,
968                    <classname>IntegerParameterType</classname>,
969                    <classname>LongParameterType</classname>
970                  </term>
971                  <listitem>
972                    <para>
973                      Asks for numerical values. Includes options for
974                      specifying a range (min/max) of allowed values.
975                    </para>
976                  </listitem>
977                </varlistentry>
978                <varlistentry>
979                  <term>
980                    <classname>BooleanParameterType</classname>
981                  </term>
982                  <listitem>
983                    <para>Asks for a boolean value.
984                    </para>
985                  </listitem>
986                </varlistentry>
987                <varlistentry>
988                  <term>
989                    <classname>DateParameterType</classname>
990                  </term>
991                  <listitem>
992                    <para>Asks for a date.
993                    </para>
994                  </listitem>
995                </varlistentry>
996                <varlistentry>
997                  <term>
998                    <classname>FileParameterType</classname>
999                  </term>
1000                  <listitem>
1001                    <para>Asks for a file item.
1002                    </para>
1003                  </listitem>
1004                </varlistentry>
1005                <varlistentry>
1006                  <term>
1007                    <classname>ItemParameterType</classname>
1008                  </term>
1009                  <listitem>
1010                    <para>
1011                      Asks for any other item. This parameter type requires
1012                      that a list of options is supplied, except when the item
1013                      type asked for matches the current <classname>GuiContext</classname>, in which
1014                      case the currently selected item is used as the
1015                      parameter value.
1016                    </para>
1017                  </listitem>
1018                </varlistentry>
1019                <varlistentry>
1020                  <term>
1021                    <classname>PathParameterType</classname>
1022                  </term>
1023                  <listitem>
1024                    <para>
1025                      Ask for a path to a file or directory. The path may be
1026                      non-existing and should be used when a plug-in needs an
1027                      output destination, i.e., the file to export to, or a
1028                      directory where the output files should be placed.
1029                    </para>
1030                  </listitem>
1031                </varlistentry>
1032              </variablelist>
1033              <para>
1034                You can also create a
1035                <classname>PluginParameter</classname>
1036                with a null name and
1037                <classname>ParameterType</classname>.
1038                In this case, the web client will not ask for input from the user, instead
1039                it is used as a section header, allowing you to group parameters into
1040                different sections which increase the readability of the input
1041                parameters page.
1042              </para>
1043<programlisting>PluginParameter firstSection = new PluginParameter(null, "First section", null, null);
1044PluginParameter secondSection = new PluginParameter(null, "Second section", null, null);
1045// ...
1046
1047parameters.add(firstSection);
1048parameters.add(firstParameterInFirstSection);
1049parameters.add(secondParameteInFirstSection);
1050
1051parameters.add(secondSection);
1052parameters.add(firstParameterInSecondSection);
1053parameters.add(secondParameteInSecondSection);</programlisting>
1054            </listitem>
1055          </varlistentry>
1056          <varlistentry>
1057            <term>
1058              <methodsynopsis language="java">
1059                <modifier>public</modifier>
1060                <void />
1061                <methodname>configure</methodname>
1062                <methodparam>
1063                  <type>GuiContext</type>
1064                  <parameter>context</parameter>
1065                </methodparam>
1066                <methodparam>
1067                  <type>Request</type>
1068                  <parameter>request</parameter>
1069                </methodparam>
1070                <methodparam>
1071                  <type>Response</type>
1072                  <parameter>response</parameter>
1073                </methodparam>
1074              </methodsynopsis>
1075            </term>
1076            <listitem>
1077              <para>
1078                Sends parameter values entered by the user for processing by the plug-in.
1079                The plug-in must validate that the parameter values are
1080                correct and then store them in database.
1081              </para>
1082             
1083              <important>
1084                <para>
1085                No validation is done by the core, except converting the input to the
1086                correct object type, i.e. if the plug-in asked for a
1087                <classname>Float</classname>
1088                the input string is parsed and converted to a
1089                <classname>Float</classname>. If you have extended the
1090                <classname>AbstractPlugin</classname>
1091                class it is very easy to validate the parameters with the
1092                <methodname>validateRequestParameters()</methodname>
1093                method. This method takes the same list of
1094                <classname>PluginParameter</classname>:s
1095                as used in the
1096                <classname>RequestInformation</classname>
1097                object and uses that information for validation. It returns null or a
1098                list of
1099                <exceptionname>Throwable</exceptionname>:s that can be
1100                given directly to the <code>response.setError()</code>
1101                methods.
1102                </para>
1103              </important>
1104              <para>
1105                When the parameters have been validated, they need to be stored
1106                in the database. Once again, it is very easy, if you use one of the
1107                <methodname>AbstractPlugin.storeValue()</methodname>
1108                or
1109                <methodname>AbstractPlugin.storeValues()</methodname>
1110                methods.
1111              </para>
1112              <para>
1113                The configure method works much like the <methodname>Plugin.run()</methodname> 
1114                method. It must return the result in the <classname>Response</classname> object,
1115                and shouldn't trow any exceptions.
1116              </para>
1117             
1118              <example id="net.sf.basedb.core.plugin.InteractivePlugin.configure">
1119                <title>
1120                  Configuration implementation building on the examples above
1121                </title>
1122<programlisting>public void configure(GuiContext context, Request request, Response response)
1123{
1124   String command = request.getCommand();
1125   try
1126   {
1127      if (command.equals(Request.COMMAND_CONFIGURE_PLUGIN))
1128      {
1129         // TODO
1130      }
1131      else if (command.equals(Request.COMMAND_CONFIGURE_JOB))
1132      {
1133         // Validate user input
1134         List&lt;Throwable&gt; errors =
1135            validateRequestParameters(getConfigureJob().getParameters(), request);
1136         if (errors != null)
1137         {
1138            response.setError(errors.size() +
1139               " invalid parameter(s) were found in the request", errors);
1140            return;
1141         }
1142         
1143         // Store user input
1144         storeValue(job, request, fileParameter);
1145         storeValue(job, request, updateExistingParameter);
1146         
1147         // We are happy and done
1148         response.setDone("Job configuration complete", Job.ExecutionTime.SHORT);
1149         // TODO - check file size to make a better estimate of execution time
1150      }
1151   }
1152   catch (Throwable ex)
1153   {
1154      response.setError(ex.getMessage(), Arrays.asList(ex));
1155   }
1156}</programlisting>
1157              </example>
1158             
1159              <para>
1160                Note that the call to
1161                <methodname>response.setDone()</methodname>
1162                has a second parameter
1163                <constant>Job.ExecutionTime.SHORT</constant>. It is an indication
1164                about how long time it will take to execute the
1165                plug-in. This is of interest for job queue managers which probably
1166                doesn't want to start too many long-running jobs at the same time
1167                blocking the entire system. Please try to use this parameter wisely and
1168                not use <constant>Job.ExecutionTime.SHORT</constant>
1169                out of old habit all the time.
1170              </para>
1171              <para>
1172                The <classname>Response</classname> class also has a
1173                <methodname>setContinue()</methodname>
1174                method which tells the core that the plug-in needs more parameters,
1175                i.e. the core will then call
1176                <methodname>getRequestInformation()</methodname>
1177                again with the new command, let the user enter values, and then call
1178                <methodname>configure()</methodname>
1179                with the new values. This process is repeated until the plug-in
1180                reports that it is done or an error occurs.
1181              </para>
1182              <para>
1183                An important note is that during this iteration it is the same instance
1184                of the plug-in that is used. However, no parameter values are stored in
1185                the database until the plugin sends a
1186                <methodname>response.setDone()</methodname>.
1187                After that, the plug-in instance is usually discarded, and a job is placed
1188                in the job queue. The execution of the plug-in happens in a new instance
1189                and maybe on a different server.
1190              </para>
1191              <tip>
1192                  <para>
1193                    You don't have to store all values the plug-in asked for in the
1194                    first place. You may even choose to store different values than
1195                    those that were entered. For example, you might ask for the mass
1196                    and height of a person and then only store the body mass index,
1197                    which is calculated from those values.
1198                  </para>
1199              </tip>
1200            </listitem>
1201          </varlistentry>
1202        </variablelist>
1203      </sect3>
1204   
1205    </sect2>
1206 
1207    <sect2 id="plugin_developer.api.callsequence">
1208      <title>What happens when...</title>
1209     
1210      <para>
1211        This section describes how the BASE core interacts with the
1212        plug-in in a number of use cases. We will outline the
1213        order the methods are invoked on the plug-in.
1214      </para>
1215     
1216      <sect3 id="plugin_developer.api.callsequence.install">
1217        <title>Installing a plug-in</title>
1218       
1219        <para>
1220          When a plug-in is installed the core is eager to find out
1221          information about the plug-in. To do this it calls the
1222          following methods in this order:
1223        </para>
1224       
1225        <orderedlist>
1226        <listitem>
1227          <para>
1228          A new instance of the plug-in class is created. The plug-in must
1229          have a public no-argument constructor.
1230          </para>
1231        </listitem>
1232       
1233        <listitem>
1234          <para>
1235          Calls are made to <methodname>Plugin.getMainType()</methodname>,
1236          <methodname>Plugin.supportsConfigurations()</methodname>,
1237          <methodname>Plugin.requiresConfiguration()</methodname> and
1238          <methodname>Plugin.getAbout()</methodname> to find out information
1239          about the plug-in. This is the only time theese methods are called.
1240          The information that is returned by them are copied and stored in
1241          the database for easy access.
1242          </para>
1243         
1244          <note>
1245            <para>
1246            The <methodname>Plugin.init()</methodname> method is
1247            never called during plug-in installation.
1248            </para>
1249          </note>
1250        </listitem>
1251       
1252        <listitem>
1253          <para>
1254          If the plug-in implements the <interfacename>InteractivePlugin</interfacename>
1255          interface the <methodname>InteractivePlugin.getGuiContexts()</methodname>
1256          method is called. This is the only time this method. The information it
1257          returns are copied and stored in the database.
1258          </para>
1259        </listitem>
1260       
1261        <listitem>
1262          <para>
1263          If the server admin decided to use the plug-in permission system, the
1264          <methodname>Plugin.getPermissions()</methodname> method is called.
1265          The returned information is copied and stored in the database.
1266          </para>
1267        </listitem>
1268       
1269        </orderedlist>
1270      </sect3>
1271   
1272      <sect3 id="plugin_developer.api.callsequence.configure">
1273        <title>Configuring a plug-in</title>
1274       
1275        <para>
1276          The plug-in must implement the <interfacename>InteractivePlugin</interfacename>
1277          interface and the <methodname>Plugin.supportsConfigurations()</methodname> method
1278          must return <constant>TRUE</constant>. The configuration is done with
1279          a wizard-like interface (see <xref linkend="plugins.configuration.wizard" />).
1280          The same plug-in instance is used throughout the entire configuration sequence.
1281        </para>
1282       
1283        <orderedlist>
1284        <listitem>
1285          <para>
1286          A new instance of the plug-in class is created. The plug-in must
1287          have a public no-argument constructor.
1288          </para>
1289        </listitem>
1290       
1291        <listitem>
1292          <para>
1293          The <methodname>Plugin.init()</methodname> method is called. The
1294          <varname>job</varname> parameter is null.
1295          </para>
1296        </listitem>
1297       
1298        <listitem id="plugin_developer.api.callsequence.configure.requestinformation">
1299          <para>
1300          The <methodname>InteractivePlugin.getRequestInformation()</methodname> 
1301          method is called. The <varname>context</varname> parameter is <constant>null</constant>
1302          and the <varname>command</varname> is the value of the string
1303          constant <constant>Request.COMMAND_CONFIGURE_PLUGIN</constant> 
1304          (_config_plugin).
1305          </para>
1306        </listitem>
1307       
1308        <listitem id="plugin_developer.api.callsequence.configure.form">
1309          <para>
1310          The web client process this information and displays a form for user
1311          input. The plug-in will have to wait some time while the user enters
1312          data.
1313          </para>
1314        </listitem>
1315       
1316        <listitem>
1317          <para>
1318          The <methodname>InteractivePlugin.configure()</methodname> method
1319          is called. The <varname>context</varname> parameter is still
1320          <constant>null</constant> and the <varname>request</varname>
1321          parameter contains the parameter values entered by the user.
1322          </para>
1323        </listitem>
1324       
1325        <listitem>
1326          <para>
1327          The plug-in must validate the values and decide whether they should be
1328          stored in the database or not. We recommend that you use the
1329          methods in the <classname>AbstractPlugin</classname> class for this.
1330          </para>
1331        </listitem>
1332       
1333        <listitem>
1334          <para>
1335          The plug-in has three different respones:
1336         
1337          <itemizedlist>
1338          <listitem>
1339            <para>
1340            <methodname>Response.setDone()</methodname>: The configuration
1341            is complete. The core will write any configuation changes to the
1342            database, call the <methodname>Plugin.done()</methodname> method and
1343            then discard the plug-in instance.
1344            </para>
1345          </listitem>
1346         
1347          <listitem>
1348            <para>
1349            <methodname>Response.setError()</methodname>: There was one or more
1350            errors. The web client will display the error messages for the user and
1351            allow the user to enter new values. The process continues with
1352            step <xref linkend="plugin_developer.api.callsequence.configure.form" />.
1353            </para>
1354          </listitem>
1355         
1356          <listitem>
1357            <para>
1358            <methodname>Response.setContinue()</methodname>: The parameters are correct
1359            but the plug-in wants more parameters. The process continues with
1360            step <xref linkend="plugin_developer.api.callsequence.configure.requestinformation" />.
1361            </para>
1362          </listitem>
1363          </itemizedlist>
1364         
1365          </para>
1366        </listitem>
1367        </orderedlist>
1368
1369      </sect3>   
1370   
1371      <sect3 id="plugin_developer.api.callsequence.context">
1372        <title>Checking if a plug-in can be used in a given context</title>
1373       
1374        <para>
1375          If the plug-in is an <interfacename>InteractivePlugin</interfacename>
1376          it has specified in which contexts it can be used by the
1377          information returned from <methodname>InteractivePlugin.getGuiContexts()</methodname>
1378          method. The web client uses this information to decide whether,
1379          for example, a <guibutton>Run plugin</guibutton>
1380          button should be displayed on a page or not. However, this is not
1381          always enough to know whether the plug-in can be used or not.
1382          For example, a raw data importer plug-in can't be used to
1383          import raw data if the raw bioassay already has data.
1384          So, when the user clicks the button, the web client will
1385          load all plug-ins that possibly can be used in the given context
1386          and let each one of them check whether they can be used or not.
1387        </para>
1388       
1389     
1390        <orderedlist>
1391        <listitem>
1392          <para>
1393          A new instance of the plug-in class is created. The plug-in must
1394          have a public no-argument constructor.
1395          </para>
1396        </listitem>
1397       
1398        <listitem>
1399          <para>
1400          The <methodname>Plugin.init()</methodname> method is called.
1401          The <varname>job</varname> parameter is <constant>null</constant>.
1402          The <varname>configuration</varname> parameter is <constant>null</constant>
1403          if the plug-in doesn't have any configuration parameters.
1404          </para>
1405        </listitem>
1406       
1407        <listitem>
1408          <para>
1409          The <methodname>InteractivePlugin.isInContext()</methodname>
1410          is called. If the context is a list context, the <varname>item</varname>
1411          parameter is null, otherwise the current item is passed. The plug-in
1412          should return <constant>null</constant> if it can be used under the
1413          current circumstances, or a message explaining why not.
1414          </para>
1415        </listitem>
1416       
1417        <listitem>
1418          <para>
1419            After this, the plug-in instance is discarded. If there are
1420            several configurations for a plug-in, this procedure is repeated
1421            for each configuration.
1422          </para>
1423
1424          <warning>
1425          <para>
1426            The <methodname>Plugin.done()</methodname> method is never
1427            called! This is bug that has been reported to the developers
1428            and hopefully it will be fixed shortly.
1429          </para>
1430          </warning>
1431        </listitem>
1432        </orderedlist>
1433      </sect3>
1434   
1435      <sect3 id="plugin_developer.api.callsequence.job">
1436        <title>Creating a new job</title>
1437       
1438        <para>
1439          If the web client found that the plug-in could be
1440          used in a given context and the user selected the plug-in
1441          the job configuration sequence is started. It is a wizard-like interface
1442          identical to the configuration wizard. In fact, the same JSP pages,
1443          and calling sequence is used. See <xref linkend="plugin_developer.api.callsequence.configure" />.
1444          We don't repeat everything here. There are a few differences:
1445        </para>
1446       
1447        <itemizedlist>
1448        <listitem>
1449          <para>
1450          The <varname>job</varname> parameter is not null, but it doesn't contain
1451          any parameter values to start with. The plug-in should use this
1452          object to store job-related parameter values. The
1453          <varname>configuration</varname> parameter is <constant>null</constant> 
1454          if the plug-in is started without configuration. In any case,
1455          the configuration values are write-protected and can't be modified.
1456          </para>
1457        </listitem>
1458       
1459        <listitem>
1460          <para>
1461          The first call to <methodname>InteractivePlugin.getRequestInformation()</methodname>
1462          is done with <constant>Request.COMMAND_CONFIGURE_JOB</constant> (_configjob)
1463          as the command. The <varname>context</varname> parameter reflects the
1464          current context.
1465          </para>
1466        </listitem>
1467       
1468        <listitem>
1469          <para>
1470          When calling <methodname>Response.setDone()</methodname> the plug-in
1471          should use the variant that takes an estimated execution time.
1472          If the plug-in has support for immediate execution or download
1473          (export plug-ins only), it can also respond with
1474          <methodname>Response.setExecuteImmediately()</methodname> or
1475          <methodname>Response.setDownloadImmediately()</methodname>.
1476          </para>
1477         
1478          <para>
1479          If the plug-in requested and was granted immediate execution or
1480          download the same plug-in instance is used to execute the plug-in.
1481          This may be done with the same or a new thread. Otherwise, a new
1482          job is added to the job queue, the parameter value are saved
1483          and the plug-in instance is discarded after calling the
1484          <methodname>Plugin.done()</methodname> method.
1485          </para>
1486        </listitem>
1487        </itemizedlist>
1488      </sect3>
1489   
1490      <sect3 id="plugin_developer.api.callsequence.execute">
1491        <title>Executing a job</title>
1492       
1493        <para>
1494          Normally, the creation of a job and the execution of it are
1495          two different events. The execution may as well be done on a
1496          different server. See <xref linkend="installation_upgrade.jobagents" />.
1497          This means that the execution takes place in a different instance
1498          of the plug-in class than what was used for creating the job.
1499          The exception is if a plug-in supports immediate execution or download.
1500          In this case the same instance is used, and it, of course,
1501          is always executed on the web server.
1502        </para>
1503       
1504        <orderedlist>
1505        <listitem>
1506          <para>
1507          A new instance of the plug-in class is created. The plug-in must
1508          have a public no-argument constructor.
1509          </para>
1510        </listitem>
1511       
1512        <listitem>
1513          <para>
1514          The <methodname>Plugin.init()</methodname> method is called.
1515          The <varname>job</varname> parameter contains the job
1516          configuration paramters. The <varname>configuration</varname> parameter
1517          is <constant>null</constant> if the plug-in doesn't have any
1518          configuration parameters.
1519          </para>
1520        </listitem>
1521       
1522        <listitem>
1523          <para>
1524          The <methodname>Plugin.run()</methodname> method is called.
1525          It is finally time for the plug-in to do the work it has bee
1526          designed for. This method shouldn't throw any exceptions.
1527          Use the <methodname>Response.setDone()</methodname>
1528          method to report success or the <methodname>Response.setError()</methodname>
1529          to reporter errors.
1530          </para>
1531        </listitem>
1532       
1533        <listitem>
1534          <para>
1535          In both cases the <methodname>Plugin.done()</methodname>
1536          method is called and the plug-in instance is discarded.
1537          </para>
1538        </listitem>
1539       
1540        </orderedlist>
1541      </sect3>
1542   
1543    </sect2>
1544 
1545    <sect2 id="plugin_developer.api.jspparameters">
1546      <title>Using custom JSP pages for parameter input</title>
1547
1548        <para>
1549          This is an advanced option for plug-ins that require a different interface for
1550          specifying plug-in parameters than the default list showing each parameter at a
1551          time. This feature is used by setting the RequestInformation.getJspPage()
1552          property when constructing the request information object. If this property has
1553          a non-null value, the web client will send the browser to the specified JSP page
1554          instead of to the generic parameter input page.
1555        </para>
1556        <para>
1557          When setting the JSP page you should only set the file name. Don't include
1558          any path information. The web
1559          client has a special location for these JSP pages, generated from the package
1560          name of your plug-in. If the plug-in is located in the package
1561          <classname>org.company</classname>
1562          the JSP page must be located in
1563          <filename class="directory">&lt;base-dir&gt;/www/plugins/org/company/</filename>.
1564          Please note that the browser still thinks that it is showing the regular page
1565          at the usual location:
1566          <filename class="directory">&lt;base-dir&gt;/www/common/plugin/index.jsp</filename>.
1567          All links in your JSP page should be relative to that directory.
1568        </para>
1569        <para>
1570          Even if you use your own JSP page we recommend that you use the built-in
1571          facility for passing the parameters back to the plug-in. For this to work you
1572          must:
1573        </para>
1574        <itemizedlist spacing="compact">
1575          <listitem>
1576            <simpara>
1577            Generate the list of <classname>PluginParameter</classname> 
1578            objects as usual.
1579            </simpara>
1580          </listitem>
1581          <listitem>
1582            <simpara>
1583              Name all your input fields in the JSP like:
1584              <parameter>
1585                parameter:<replaceable>name-of-parameter</replaceable>
1586              </parameter>
1587            </simpara>
1588<programlisting>// Plugin generate PluginParameter
1589StringParameterType stringPT = new StringParameterType(255, null, true);
1590PluginParameter one = new PluginParameter("one", "One", "First string", stringPT);
1591PluginParameter two = new PluginParameter("two", "Two", "Second string", stringPT);
1592
1593// JSP should name fields as:
1594First string: &lt;input type="text" name="parameter:one"&gt;&lt;br&gt;
1595Second string: &lt;input type="text" name="parameter:two"&gt;</programlisting>
1596          </listitem>
1597          <listitem>
1598          <simpara>
1599            Send the form to
1600            <filename>index.jsp</filename>
1601            with the <varname>ID</varname> and <varname>cmd</varname> parameters
1602            as shown below.
1603          </simpara>
1604<programlisting>&lt;form action="index.jsp" method="post"&gt;
1605&lt;input type="hidden" name="ID" value="&lt;%=ID%&gt;"&gt;
1606&lt;input type="hidden" name="cmd" value="SetParameters"&gt;
1607...
1608&lt;/form&gt;</programlisting>
1609
1610          </listitem>
1611          </itemizedlist>
1612            <para>
1613              In your JSP page you will probably need to access some information like the
1614              <classname>SessionControl</classname>, <classname>Job</classname>
1615              and possible even the <classname>RequestInformation</classname>
1616              object created by your plug-in.
1617            </para>
1618<programlisting>// Get session control and it's ID (required to post to index.jsp)
1619final SessionControl sc = Base.getExistingSessionControl(pageContext, true);
1620final String ID = sc.getId();
1621
1622// Get information about the current request to the plug-in
1623PluginConfigurationRequest pcRequest =
1624   (PluginConfigurationRequest)sc.getSessionSetting("plugin.configure.request");
1625PluginDefinition plugin =
1626   (PluginDefinition)sc.getSessionSetting("plugin.configure.plugin");
1627PluginConfiguration pluginConfig =
1628   (PluginConfiguration)sc.getSessionSetting("plugin.configure.config");
1629PluginDefinition job =
1630   (PluginDefinition)sc.getSessionSetting("plugin.configure.job");
1631RequestInformation ri = pcRequest.getRequestInformation();</programlisting>
1632
1633    </sect2>
1634  </sect1>
1635
1636  <sect1 id="plugin_developer.import">
1637    <title>Import plug-ins</title>
1638    <para>
1639   
1640      This documentation is only available in the old format.
1641      See <ulink url="http://base.thep.lu.se/chrome/site/doc/development/plugins/import/index.html"
1642        >http://base.thep.lu.se/chrome/site/doc/development/plugins/import/index.html</ulink>
1643    </para>
1644   
1645    <sect2 id="plugin_developer.import.autodetect">
1646      <title>Autodetecting importer</title>
1647      <para></para>
1648    </sect2>
1649   
1650    <sect2 id="plugin_developer.import.abstractflatfileimporter">
1651      <title>The AbstractFlatFileImporter superclass</title>
1652      <para></para>
1653    </sect2>
1654  </sect1>
1655
1656  <sect1 id="plugin_developer.export">
1657    <title>Export plug-ins</title>
1658   
1659    <para>
1660      Export plug-ins are plug-ins that takes data from BASE, and
1661      prepares it for use with some external entity. Usually this
1662      means that data is taken from the database and put into a file
1663      with some well-defined file format.
1664      An export plug-in should return <constant>MainType.EXPORT</constant> from
1665      the <methodname>Plugin.getMainType()</methodname> method.
1666    </para>
1667   
1668    <sect2 id="plugin_developer.export.download">
1669      <title>Immediate download of exported data</title>
1670      <para>
1671        An export plug-in may want to give the user a choice
1672        between saving the exported data in the BASE file system
1673        or to download it immediately to the client computer. With the basic
1674        plug-in API the second option is not possible. The
1675        <interfacename>ImmediateDownloadExporter</interfacename> is an
1676        interface that extends the <interfacename>Plugin</interfacename>
1677        interface to provide this functionality. If your export
1678        plug-in wants to provide immediate download functionality it must
1679        implement the <interfacename>ImmediateDownloadExporter</interfacename>
1680        interface.
1681      </para>
1682     
1683      <sect3 id="plugin_developer.export.immediatedownloadexporter">
1684      <title>The ImmediateDownloadExporter interface</title>
1685      <variablelist>
1686      <varlistentry>
1687        <term>
1688          <methodsynopsis language="java">
1689            <modifier>public</modifier>
1690            <void/>
1691            <methodname>doExport</methodname>
1692            <methodparam>
1693              <type>ExportOutputStream</type>
1694              <parameter>out</parameter>
1695            </methodparam>
1696            <methodparam>
1697              <type>ProgressReporter</type>
1698              <parameter>progress</parameter>
1699            </methodparam>
1700          </methodsynopsis>
1701        </term>
1702        <listitem>
1703          <para>
1704          Perform the export. The plug-in should write the
1705          exported data to the <varname>out</varname> stream.
1706          If the <varname>progress</varname> parameter isn't null,
1707          the progress should be reported at regular interval in the
1708          same manner as in the <methodname>Plugin.run()</methodname>
1709          method.
1710          </para>
1711        </listitem>
1712      </varlistentry>
1713      </variablelist>
1714     
1715      </sect3>
1716     
1717      <sect3 id="plugin_developer.export.exportoutputstream">
1718      <title>The ExportOutputStream class</title>
1719     
1720      <para>
1721        The <classname>ExportOutputStream</classname> is extends
1722        <classname>java.io.OutputStream</classname>. Use the regular
1723        <methodname>write()</methodname> methods to write data to it.
1724        It also has three additional methods, which are used to generate
1725        HTTP response headers.
1726      </para>
1727     
1728      <note>
1729        <para>
1730        These methods must be called before starting to write data to
1731        the <varname>out</varname> stream.
1732        </para>
1733      </note>
1734     
1735      <variablelist>
1736      <varlistentry>
1737        <term>
1738          <methodsynopsis language="java">
1739            <modifier>public</modifier>
1740            <void/>
1741            <methodname>setContentLength</methodname>
1742            <methodparam>
1743              <type>long</type>
1744              <parameter>contentLength</parameter>
1745            </methodparam>
1746          </methodsynopsis>
1747        </term>
1748        <listitem>
1749          <para>
1750          Set the total size of the exported data (if known).
1751          </para>
1752        </listitem>
1753      </varlistentry>
1754      <varlistentry>
1755        <term>
1756          <methodsynopsis language="java">
1757            <modifier>public</modifier>
1758            <void/>
1759            <methodname>setMimeType</methodname>
1760            <methodparam>
1761              <type>String</type>
1762              <parameter>mimeType</parameter>
1763            </methodparam>
1764          </methodsynopsis>
1765        </term>
1766        <listitem>
1767          <para>
1768          Set the MIME type of the file that is beeing generated.
1769          </para>
1770        </listitem>
1771      </varlistentry>
1772      <varlistentry>
1773        <term>
1774          <methodsynopsis language="java">
1775            <modifier>public</modifier>
1776            <void/>
1777            <methodname>setFilename</methodname>
1778            <methodparam>
1779              <type>String</type>
1780              <parameter>filename</parameter>
1781            </methodparam>
1782          </methodsynopsis>
1783        </term>
1784        <listitem>
1785          <para>
1786          Set a suggested name of the file that is beeing
1787          generated.
1788          </para>
1789        </listitem>
1790      </varlistentry>
1791      </variablelist>
1792     
1793      </sect3>
1794     
1795      <sect3 id="plugin_developer.export.callsequence">
1796        <title>Call sequence during immediate download</title>
1797
1798      <para>
1799        Supporting immediate download also means that the method call
1800        sequence is a bit altered from the standard sequence described
1801        in <xref linkend="plugin_developer.api.callsequence.execute" />.
1802      </para>
1803     
1804      <itemizedlist>
1805      <listitem>
1806        <para>
1807        The plug-in must call <methodname>Response.setDownloadImmediately()</methodname>
1808        instead of <methodname>Response.setDone()</methodname> in <methodname>Plugin.configure()</methodname>
1809        to to end the job configuration wizard. This requests that the core starts
1810        an immediate download.
1811        </para>
1812       
1813        <note>
1814          <para>
1815          Even if an immediate download is requested by the plug-in this feature
1816          may have been disabled by the server administrator. If so, the plug-in
1817          can choose if the job should be added to job queue or if this is an
1818          error condition.
1819          </para>
1820        </note>
1821      </listitem>
1822     
1823      <listitem>
1824        <para>
1825        If immediate download is granted the web client will keep the
1826        same plug-in instance and call <methodname>ImmediateDownloadExporter.doExport()</methodname>.
1827        In this case, the <methodname>Plugin.run()</methodname> is never called.
1828       
1829        <note>
1830          <para>
1831          The <methodname>Plugin.done()</methodname> method is also never
1832          called. This is a bug which has been reported to the developers.
1833          </para>
1834        </note>
1835        </para>
1836       
1837      </listitem>
1838
1839      <listitem>
1840        <para>
1841        If immediate download isn't granted and the job is added to the job queue
1842        the regular job execution sequence is used. That is, the
1843        <methodname>Plugin.run()</methodname> method is called.
1844        </para>
1845      </listitem>
1846      </itemizedlist>
1847     
1848      </sect3>
1849
1850    </sect2>
1851   
1852    <sect2 id="plugin_developer.export.abstractexporter">
1853      <title>The AbstractExporterPlugin class</title>
1854   
1855      <para>
1856        This is an abstract superclass that will make it easier
1857        to implement export plug-ins that support immediate
1858        download. It defines <classname>PluginParameter</classname>
1859        objects for asking a user about a path where the exported
1860        data should be saved and if existing files should be overwritten or not.
1861        If the user leaves the path empty the immediate download functionality
1862        should be used. It also contains implementations of both the
1863        <methodname>Plugin.run()</methodname> method and the
1864        <methodname>ImmediateDownloadExporter.doExport()</methodname> method.
1865        Here is what you need to do in your own plug-in code (code examples are
1866        take from the <classname>HelpExporter</classname>):
1867      </para>
1868     
1869      <itemizedlist>
1870      <listitem>
1871        <para>
1872          Your plug-in should extend the <classname>AbstractExporterPlugin</classname>
1873          class:
1874          <programlisting>
1875public class HelpExporter
1876  extends AbstractExporterPlugin
1877  implements InteractivePlugin
1878</programlisting>
1879        </para>
1880      </listitem>
1881     
1882      <listitem>
1883        <para>
1884          You need to implement the
1885          <methodname>InteractivePlugin.getRequestInformation()</methodname>
1886          method. Use the <methodname>getSaveAsParameter()</methodname>
1887          and <methodname>getOverwriteParameter()</methodname> methods defined in the
1888          superclass to create plug-in parameters that asks for the file name to save
1889          to and if existing files can be overwritten or not.
1890          You should also check if the administrator has enabled the immediate execution
1891          functionality for your plug-in. If not, the only option is to
1892          export to a file in the BASE file system and the filename is a
1893          required parameter.
1894         
1895          <programlisting>
1896// Selected parts of the getRequestConfiguration() method
1897...
1898List&lt;PluginParameter&lt;?&gt;&gt; parameters =
1899   new ArrayList&lt;PluginParameter&lt;?&gt;&gt;();
1900...
1901PluginDefinition pd = job.getPluginDefinition();
1902boolean requireFile = pd == null ?
1903   false : !pd.getAllowImmediateExecution();
1904
1905parameters.add(getSaveAsParameter(null, null, defaultPath, requireFile));
1906parameters.add(getOverwriteParameter(null, null));
1907
1908configureJob = new RequestInformation
1909(
1910   Request.COMMAND_CONFIGURE_JOB,
1911   "Help exporter options",
1912   "Set Client that owns the helptexts, " +
1913     "the file path where the export file should be saved",
1914   parameters
1915);
1916....
1917return configureJob;
1918</programlisting>
1919        </para>
1920      </listitem>
1921
1922      <listitem>
1923        <para>
1924        You must also implement the <methodname>configure()</methodname>
1925        method and check the parameters. If no filename has been given,
1926        you should check if immediate exection is allowed and set an
1927        error if it isn't. If a filename is present, use the
1928        <methodname>pathCanBeUsed()</methodname> method to check if
1929        it is possible to save the data to a file with that name. If the
1930        file already exists it can be overwritten if the <varname>OVERWRITE</varname>
1931        is <constant>TRUE</constant> or if the file has been flagged for removal.
1932        Don't forget to store the parameters with the <methodname>storeValue()</methodname>
1933        method.
1934
1935        <programlisting>
1936// Selected parts from the configure() method
1937if (request.getParameterValue(SAVE_AS) == null)
1938{
1939   if (!request.isAllowedImmediateExecution())
1940   {
1941      response.setError("Immediate download is not allowed. " +
1942         "Please specify a filename.", null);
1943      return;
1944   }
1945   Client client = (Client)request.getParameterValue("client");
1946   response.setDownloadImmediately("Export help texts for client application " +
1947     client.getName(), ExecutionTime.SHORTEST, true);
1948}
1949else
1950{
1951   if (!pathCanBeUsed((String)request.getParameterValue(SAVE_AS),
1952      (Boolean)request.getParameterValue(OVERWRITE)))
1953   {
1954      response.setError("File exists: " +
1955         (String)request.getParameterValue(SAVE_AS), null);
1956      return;
1957   }
1958   storeValue(job, request, ri.getParameter(SAVE_AS));
1959   storeValue(job, request, ri.getParameter(OVERWRITE));
1960   response.setDone("The job configuration is complete", ExecutionTime.SHORTEST);
1961}
1962</programlisting>
1963       
1964        </para>
1965      </listitem>
1966     
1967      <listitem>
1968        <para>
1969        Implement the <methodname>performExport()</methodname> method.
1970        This is defined as abstract in the <classname>AbstractExporterPlugin</classname>
1971        class. It has the same parameters as the <methodname>ImmediateDownloadExporter.doExport()</methodname>
1972        method and they have the same meaning. The only difference is that the
1973        <varname>out</varname> stream can be linked to a file and not just to the
1974        immediate download.
1975        </para>
1976      </listitem>
1977     
1978      <listitem>
1979        <para>
1980        Optionally, implement the <methodname>begin()</methodname>,
1981        <methodname>end()</methodname> and <methodname>getSuccessMessage()</methodname>
1982        methods. Theese methods do nothing by default.
1983        </para>
1984      </listitem>
1985      </itemizedlist>
1986     
1987      <para>
1988        The call sequence for plug-ins extending <classname>AbstractExporterPlugin</classname>
1989        is:
1990      </para>
1991     
1992      <orderedlist>
1993      <listitem>
1994        <para>
1995        Call <methodname>begin()</methodname>.
1996        </para>
1997      </listitem>
1998      <listitem>
1999        <para>
2000        Call <methodname>performExport()</methodname>.
2001        </para>
2002      </listitem>
2003      <listitem>
2004        <para>
2005        Call <methodname>end()</methodname>.
2006        </para>
2007      </listitem>
2008      <listitem>
2009        <para>
2010        Call <methodname>getSuccessMessage()</methodname> if running as a regular
2011        job. This method is never called when doing an immediate download since there
2012        is no place to show the message.
2013        </para>
2014      </listitem>
2015      </orderedlist>
2016     
2017    </sect2>
2018   
2019  </sect1>
2020 
2021  <sect1 id="plugin_developer.analyse">
2022    <title>Analysis plug-ins</title>
2023    <para>
2024      A plug-in becomes an analysis plug-in simply by returning
2025      <constant>Plugin.MainType.ANALYSIS</constant> from the
2026      <methodname>Plugin.getMainType()</methodname> method. The information returned from
2027      <methodname>InteractivePlugin.getGuiContexts()</methodname>
2028      must include: [<constant>Item.BIOASSAYSET</constant>, <constant>Type.ITEM</constant>] since
2029      this is the only place where the web client looks for analysis plug-ins.
2030    </para>
2031   
2032<programlisting>private static final Set&lt;GuiContext&gt; guiContexts =
2033   Collections.singleton(new GuiContext(Item.BIOASSAYSET, GuiContext.Type.ITEM));
2034
2035public Set&lt;GuiContext&gt; getGuiContexts()
2036{
2037   return guiContexts;
2038}</programlisting>
2039
2040      <para>
2041        More documentation is available in the old format.
2042        See <ulink url="http://base.thep.lu.se/chrome/site/doc/development/plugins/analysis/index.html"
2043          >http://base.thep.lu.se/chrome/site/doc/development/plugins/analysis/index.html</ulink>
2044      </para>
2045
2046
2047  </sect1>
2048 
2049  <sect1 id="plugin_developer.other">
2050    <title>Other plug-ins</title>
2051    <para></para>
2052   
2053    <sect2 id="plugin_developer.other.authentication">
2054      <title>Authentication plug-ins</title>
2055     
2056      <para>
2057        BASE provides a plug-in mechanism for authenticating users
2058        (validating the username and password) when they are logging in.
2059        This plug-in mechanism is not the same as the regular plug-in API.
2060        That is, you don't have worry about user interaction or implementing the
2061        <interfacename>Plugin</interfacename> interface.
2062      </para>
2063     
2064      <sect3 id="plugin_developer.other.authentication.internal_external">
2065        <title>Internal vs. external authentation</title>
2066     
2067        <para>
2068          BASE can authenticate users in two ways. Either it uses the internal
2069          authentiction or the external authentication. With internal
2070          authentication BASE stores logins and passwords in it's own database.
2071          With external authentication this is handled by some external
2072          application. Even with external authentication it is possible to
2073          let BASE cache the logins/passwords. This makes it possible to login to
2074          BASE if the external authentication server is down.
2075        </para>
2076       
2077        <note>
2078          <para>
2079          An external authentication server can only be used to grant or deny
2080          a user access to BASE. It can't be used to give a user permissions,
2081          or put a user into groups or different roles inside BASE.
2082          </para>
2083        </note>
2084
2085        <para>
2086          The external authentication service is only used when a user logs in.
2087          Now, one or more of several things can happen:
2088         
2089          <itemizedlist>
2090          <listitem>
2091            <para>
2092              The ROOT user is logging on. Internal authentication is always
2093              used for the root user and the authenticator plug-in is never
2094              used.
2095            </para>
2096          </listitem>
2097
2098          <listitem>
2099            <para>
2100              The login is correct and the user is already known to BASE.
2101              If the plug-in supports extra information (name, email, phone, etc.)
2102              and the <property>auth.synchronize</property> setting
2103              is <constant>TRUE</constant> the extra information is copied to
2104              the BASE server.
2105            </para>
2106          </listitem>
2107         
2108          <listitem>
2109            <para>
2110              The login is correct, but the user isn't known to BASE. This happens
2111              the first time a user logs in. BASE will create
2112              a new user account. If the driver supports extra information, it
2113              is copied to the BASE server (even if <property>auth.synchronize</property> 
2114              isn't set). The new user account will get the default quota
2115              and be added to the all roles and groups which has been
2116              marked as <emphasis>default</emphasis>.
2117             
2118              <note>
2119              <para>
2120                Prior to BASE 2.4 it was hardcoded to add the new user to the
2121                <emphasis>Users</emphasis> role only.
2122              </para>
2123              </note>
2124            </para>
2125          </listitem>
2126
2127          <listitem>
2128            <para>
2129              If password caching is enabled, the password is copied to BASE.
2130              If an expiration timeout has been set, an expiration date
2131              will be calculated and set on the user account. The expiration
2132              date is only checked when the external authentication server is
2133              down.
2134            </para>
2135          </listitem>
2136
2137          <listitem>
2138            <para>
2139              The authentication server says that the login is invalid or
2140              the password is incorrect. The user will not be logged in.
2141              If a user account with the specified login already exists in
2142              BASE, it will be disabled.
2143            </para>
2144          </listitem>
2145         
2146          <listitem>
2147            <para>
2148              The authentication driver says that something else is wrong.
2149              If password caching is enabled, internal authentication will be
2150              used. Otherwise the user will not be logged in. An already
2151              existing account is not modified or disabled.
2152            </para>
2153          </listitem>
2154          </itemizedlist>
2155         
2156          <note>
2157            <para>
2158            The <guilabel>Encrypt password</guilabel> option that is
2159            available on the login page doesn't work with external
2160            authentication. The simple reason is that the password is
2161            encrypted with a one-way algorithm making it impossible to
2162            call <methodname>Authenticator.authenticate()</methodname>.
2163            </para>
2164          </note>
2165         
2166        </para>
2167      </sect3>
2168     
2169      <sect3 id="plugin_developer.other.authentication.authenticator">
2170        <title>The Authenticator interface</title>
2171       
2172        <para>
2173          To be able to use external authentication you must create a class
2174          that implements the
2175          <interfacename>net.sf.based.core.authentication.Authenticator</interfacename> 
2176          interface. Specify the name of the class in the <property>auth.driver</property> 
2177          setting in <filename>base.config</filename> and
2178          it's initialisation parameters in the <property>auth.init</property> setting.
2179        </para>
2180       
2181        <para>
2182          Your class must have a public no-argument constructor. The BASE
2183          application will create only one instance of the class for lifetime
2184          of the BASE server. It must be thread-safe since it may be invoked by
2185          multiple threads at the same time. Here are the methods that you must
2186          implement
2187        </para>
2188       
2189        <variablelist>
2190        <varlistentry>
2191          <term>
2192            <methodsynopsis language="java">
2193              <modifier>public</modifier>
2194              <void/>
2195              <methodname>init</methodname>
2196              <methodparam>
2197                <type>String</type>
2198                <parameter>settings</parameter>
2199              </methodparam>
2200              <exceptionname>AuthenticationException</exceptionname>
2201            </methodsynopsis>
2202          </term>
2203          <listitem>
2204            <para>
2205            This method is called just after the object has been created with it's argument
2206            taken from the <property>auth.init</property> setting in your <filename>base.config</filename> 
2207            file. This method is only called once for an instance of the object. The syntax and meaning of
2208            the parameter is driver-dependent and should be documented by the plug-in.
2209            It is irrelevant for the BASE core.
2210            </para>
2211          </listitem>
2212        </varlistentry>
2213       
2214        <varlistentry>
2215          <term>
2216            <methodsynopsis language="java">
2217              <modifier>public</modifier>
2218              <type>boolean</type>
2219              <methodname>supportsExtraInformation</methodname>
2220            </methodsynopsis>
2221          </term>
2222          <listitem>
2223            <para>
2224            This method should simply return <constant>TRUE</constant> or <constant>FALSE</constant> 
2225            depending on if the plug-in supports extra user information or not. The only required
2226            information about a user is a unique ID and the login. Extra information includes
2227            name, address, phone, email, etc.
2228            </para>
2229          </listitem>       
2230        </varlistentry>
2231
2232        <varlistentry>
2233          <term>
2234            <methodsynopsis language="java">
2235              <modifier>public</modifier>
2236              <type>AuthenticationInformation</type>
2237              <methodname>authenticate</methodname>
2238              <methodparam>
2239                <type>String</type>
2240                <parameter>login</parameter>
2241              </methodparam>
2242              <methodparam>
2243                <type>String</type>
2244                <parameter>password</parameter>
2245              </methodparam>
2246              <exceptionname>UnknownLoginException</exceptionname>
2247              <exceptionname>InvalidPasswordException</exceptionname>
2248              <exceptionname>AuthenticationException</exceptionname>
2249            </methodsynopsis>
2250          </term>
2251          <listitem>
2252            <para>
2253            Try to authenticate a login/password combination. The plug-in should return
2254            an <classname>AuthenticationInformation</classname> object if the
2255            authentication is successful or throw an exception if not.
2256           
2257            There are three exceptions to choose from:
2258           
2259            <itemizedlist>
2260            <listitem>
2261              <para>
2262              <exceptionname>UnknownLoginException</exceptionname>:
2263              This exception should be thrown if the login is not know to the
2264              external authentication system.
2265              </para>
2266            </listitem>
2267
2268            <listitem>
2269              <para>
2270              <exceptionname>InvalidPasswordException</exceptionname>:
2271              This exception should be thrown if the login is known but the
2272              password is invalid. In case it is considered a security issue
2273              to reveal that a login exists, the plugin may throw an
2274              <exceptionname>UnknowLoginException</exceptionname> instead.
2275              </para>
2276            </listitem>
2277           
2278            <listitem>
2279              <para>
2280              <exceptionname>AuthenticationException</exceptionname>:
2281              In case there is another problem, such as the authentication service
2282              beeing down. This exception triggers the use of cached passwords
2283              if caching has been enabled.
2284              </para>
2285            </listitem>
2286            </itemizedlist>
2287            </para>
2288          </listitem>       
2289        </varlistentry>
2290        </variablelist> 
2291      </sect3>
2292     
2293      <sect3 id="plugin_developer.other.authentication.settings">
2294        <title>Configuration settings</title>
2295     
2296        <para>
2297          The configuration settings for the authentication driver are located
2298          in the <filename>base.config</filename> file.
2299          Here is an overview of the settings. For more information read
2300          <xref linkend="appendix.base.config.authentication" />.
2301        </para>
2302       
2303        <variablelist>
2304        <varlistentry>
2305          <term><property>auth.driver</property></term>
2306          <listitem>
2307            <para>
2308            The class name of the authentication plug-in.
2309            </para>
2310          </listitem>
2311        </varlistentry>
2312
2313        <varlistentry>
2314          <term><property>auth.init</property></term>
2315          <listitem>
2316            <para>
2317            Initialisation parameters sent to the plug-in when calling the
2318            <methodname>Authenticator.init()</methodname> method.
2319            </para>
2320          </listitem>
2321        </varlistentry>
2322       
2323        <varlistentry>
2324          <term><property>auth.synchronize</property></term>
2325          <listitem>
2326            <para>
2327            If extra user information is synchronized at login time or not.
2328            This setting is ignored if the driver doesn't support extra information.
2329            </para>
2330          </listitem>
2331        </varlistentry>
2332       
2333        <varlistentry>
2334          <term><property>auth.cachepasswords</property></term>
2335          <listitem>
2336            <para>
2337              If passwords should be cached by BASE or not. If the passwords are
2338              cached a user may login to BASE even if the external authentication
2339              server is down.
2340            </para>
2341          </listitem>
2342        </varlistentry>
2343       
2344        <varlistentry>
2345          <term><property>auth.daystocache</property></term>
2346          <listitem>
2347            <para>
2348              How many days to cache the passwords if caching has been enabled.
2349              A value of 0 caches the passwords for ever.     
2350            </para>
2351          </listitem>
2352        </varlistentry>
2353        </variablelist>
2354      </sect3>
2355     
2356    </sect2>
2357   
2358    <sect2 id="plugin_developer.other.secondary">
2359      <title>Secondary file storage plugins</title>
2360      <para>
2361        This documentation is only available in the old format.
2362        See <ulink url="http://base.thep.lu.se/chrome/site/doc/development/plugins/storage/index.html"
2363          >http://base.thep.lu.se/chrome/site/doc/development/plugins/storage/index.html</ulink>
2364      </para>
2365    </sect2>
2366   
2367    <sect2 id="plugin_developer.other.unpacker">
2368      <title>File unpacker plug-ins</title>
2369      <para>
2370        The BASE web client has integrated support for unpacking of
2371        compressed files. See <xref linkend="file_system.handling.upload" />.
2372        Behind the scenes, this support is provided by plug-ins. The standard
2373        BASE distribution comes with support for ZIP files
2374        (<classname>net.sf.basedb.plugins.ZipFileUnpacker</classname>).
2375      </para>
2376      <para>
2377        To add support for additional compressed formats you have to create a plug-in that
2378        implements the <interfacename>net.sf.basedb.util.zip.FileUnpacker</interfacename>
2379        interface. The best way to do this is to extend the
2380        <classname>net.sf.basedb.util.zip.AbstractFileUnpacker</classname> which
2381        implements all methods in the <interfacename>Plugin</interfacename>
2382        and <interfacename>InteractivePlugin</interfacename>
2383        interfaces except <methodname>Plugin.getAbout()</methodname>. This leaves
2384        you with the actual unpacking of the files as the only thing to implement.
2385      </para>
2386     
2387      <note>
2388        <title>No support for configurations</title>
2389        The integrated upload in the web interface only works with plug-ins that
2390        doesn't require a configuration to run.
2391      </note>
2392     
2393      <variablelist>
2394        <title>Methods in the <interfacename>FileUnpacker</interfacename> interface</title>
2395        <varlistentry>
2396          <term>
2397            <methodsynopsis language="java">
2398              <modifier>public</modifier>
2399              <type>String</type>
2400              <methodname>getFormatName</methodname>
2401            </methodsynopsis>
2402          </term>
2403          <listitem>
2404            <para>
2405            Return a short string naming the file format. For example:
2406            <constant>ZIP files</constant> or <constant>TAR files</constant>.
2407            </para>
2408          </listitem>
2409        </varlistentry>
2410           
2411        <varlistentry>
2412          <term>
2413            <methodsynopsis language="java">
2414              <modifier>public</modifier>
2415              <type>Set&lt;String&gt;</type>
2416              <methodname>getExtensions</methodname>
2417            </methodsynopsis>
2418          </term>
2419          <listitem>
2420            <para>
2421            Return a set of strings with the file extensions that
2422            are most commonly used with the compressed file format.
2423            For example: <constant>[zip, jar]</constant>. Do not include
2424            the dot in the extensions. The web client and the
2425            <methodname>AbstractFlatFileUnpacker.isInContext()</methodname> method
2426            will use this information to automatically guess which plug-in to
2427            use for unpacking the files.
2428            </para>
2429          </listitem>
2430        </varlistentry>
2431       
2432        <varlistentry>
2433          <term>
2434            <methodsynopsis language="java">
2435              <modifier>public</modifier>
2436              <type>Set&lt;String&gt;</type>
2437              <methodname>getMimeTypes</methodname>
2438            </methodsynopsis>
2439          </term>
2440          <listitem>
2441            <para>
2442            Return a set of string with the MIME types that commonly used with
2443            the compressed file format. For example:
2444            <constant>[application/zip, application/java-archive]</constant>.
2445            This information is used by the
2446            <methodname>AbstractFlatFileUnpacker.isInContext()</methodname> 
2447            method to automatically guess which plug-in to use for unpacking
2448            the files.
2449            </para>
2450          </listitem>
2451        </varlistentry>
2452       
2453        <varlistentry>
2454          <term>
2455            <methodsynopsis language="java">
2456              <modifier>public</modifier>
2457              <type>int</type>
2458              <methodname>unpack</methodname>
2459              <methodparam>
2460                <type>DbControl</type>
2461                <parameter>dc</parameter>
2462              </methodparam>
2463              <methodparam>
2464                <type>Directory</type>
2465                <parameter>dir</parameter>
2466              </methodparam>
2467              <methodparam>
2468                <type>InputStream</type>
2469                <parameter>in</parameter>
2470              </methodparam>
2471              <methodparam>
2472                <type>boolean</type>
2473                <parameter>overwrite</parameter>
2474              </methodparam>
2475              <methodparam>
2476                <type>AbsoluteProgressReporter</type>
2477                <parameter>progress</parameter>
2478              </methodparam>
2479              <exceptionname>IOException</exceptionname>
2480              <exceptionname>BaseException</exceptionname>
2481            </methodsynopsis>
2482          </term>
2483          <listitem>
2484            <para>
2485            Unpack the files and store them in the BASE file system.
2486           
2487            <itemizedlist>
2488            <listitem>
2489              <para>
2490              Do not <methodname>close()</methodname> or
2491              <methodname>commit()</methodname> the
2492              <classname>DbControl</classname> passed to this method.
2493              This is done automatically by the
2494              <classname>AbstractFileUnpacker</classname> or by the web client.
2495              </para>
2496            </listitem>
2497           
2498            <listitem>
2499              <para>
2500              The <varname>dir</varname> parameter is the root directory where
2501              the unpacked files should be placed. If the compressed file
2502              contains subdirectory the plug-in must create those subdirectories
2503              unless they already exists.
2504              </para>
2505            </listitem>
2506           
2507            <listitem>
2508              <para>
2509              If the <varname>overwrite</varname> parameter is
2510              <constant>FALSE</constant> no existing file should be overwritten
2511              unless the file is <systemitem>OFFLINE</systemitem>.
2512              </para>
2513            </listitem>
2514           
2515            <listitem>
2516              <para>
2517              The <varname>in</varname> parameter is the stream
2518              containing the compressed data. The stream may come
2519              directly from the web upload or from an existing
2520              file in the BASE file system.
2521              </para>
2522            </listitem>
2523           
2524            <listitem>
2525              <para>
2526              The <varname>progress</varname> parameter, if not
2527              <constant>null</constant>, should be used to reporter the
2528              progress back to the calling code. The plug-in should count
2529              the number of bytes read from the <varname>in</varname>
2530              stream.
2531              </para>
2532            </listitem>
2533            </itemizedlist>
2534           
2535            </para>
2536          </listitem>
2537        </varlistentry>
2538      </variablelist>
2539     
2540      <para>
2541        When the compressed file is uncompressed during the file upload
2542        from the web interface, the call sequence to the plug-in is slightly
2543        altered from the standard call sequence described in
2544        <xref linkend="plugin_developer.api.callsequence.execute" />.
2545       
2546        <itemizedlist>
2547        <listitem>
2548          <para>
2549          After the plug-in instance has been created, the
2550          <methodname>Plugin.init()</methodname> method is called with <constant>null</constant>
2551          values for both the <varname>configuration</varname> and <varname>job</varname>
2552          parameters.
2553          </para>
2554        </listitem>
2555       
2556        <listitem>
2557          <para>
2558          Then, the <methodname>unpack()</methodname> method is called. The
2559          <methodname>Plugin.run()</methodname> method is never called in this case.
2560          The <methodname>Plugin.done()</methodname> method is also never called.
2561          </para>
2562        </listitem>
2563       
2564        </itemizedlist>
2565       
2566      </para>
2567     
2568    </sect2>
2569  </sect1>
2570 
2571  <sect1 id="plugin_developer.example">
2572    <title>Example plug-ins (with download)</title>
2573    <para>
2574      <para>
2575        This documentation is only available in the old format.
2576        See <ulink url="http://base.thep.lu.se/chrome/site/doc/development/index.html#plugins"
2577          >http://base.thep.lu.se/chrome/site/doc/development/index.html#plugins</ulink>
2578      </para>
2579    </para>
2580  </sect1>
2581</chapter>
Note: See TracBrowser for help on using the repository browser.