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

Last change on this file since 3366 was 3366, checked in by Nicklas Nordborg, 16 years ago

References #551. Added section about core and web client interaction with plug-ins.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 61.9 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 3366 2007-05-23 10:56:38Z nicklas $:
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>
84&lt;?xml version="1.0" encoding="UTF-8"?&gt;
85&lt;project
86   name="MyPlugin"
87   default="build.plugin"
88   basedir="."
89   &gt;
90
91   &lt;!-- variables used --&gt;
92   &lt;property name="plugin.name" value="MyPlugin" /&gt;
93   &lt;property name="src" value="src" /&gt;
94   &lt;property name="bin" value="bin" /&gt;
95
96   &lt;!-- set up classpath for compiling --&gt;
97   &lt;path id="classpath"&gt;
98      &lt;fileset dir="lib"&gt;
99         &lt;include name="**/*.jar"/&gt;
100      &lt;/fileset&gt;
101   &lt;/path&gt;
102
103   
104   &lt;!-- main target --&gt;
105   &lt;target
106      name="build.plugin" 
107      description="Compiles the plug-in and put in jar"
108      &gt;
109      &lt;javac
110         encoding="ISO-8859-1"
111         srcdir="${src}"
112         destdir="${bin}"
113         classpathref="classpath"&gt;
114      &lt;/javac&gt;
115      &lt;jar
116         jarfile="${plugin.name}.jar"
117         basedir="bin"
118         manifest="MANIFEST.MF"
119         &gt;
120      &lt;/jar&gt;
121
122    &lt;/target&gt;
123&lt;/project&gt; 
124</programlisting>
125        </example>
126        <para>
127          If your plug-in depends on other JAR files than the
128          <filename>BASE2Core.jar</filename> you must create a file called
129          <filename>MANIFEST.MF</filename> in the project root directory.
130          List the other JAR files as in the following example.
131          If your plug-in doesn't depend on other JAR files, remove the
132          <sgmltag class="attribute">manifest</sgmltag> 
133          attribute of the <sgmltag class="starttag">jar</sgmltag> tag.
134          <programlisting>
135Manifest-Version: 1.0
136Class-Path: OtherJar.jar ASecondJar.jar
137</programlisting>
138        </para>
139      </sect3>
140       
141      <sect3 id="plugin_developer.organize.ant.build">
142        <title>Building the plug-in</title>
143        <para>
144          Compile the plug-in simply by typing
145          <command>ant</command>
146          in the console window. If all went well the
147          <filename>MyPlugin.jar</filename>
148          will be created in the same directory.
149        </para>
150        <para>
151          To install the plug-in copy the JAR file to the server including the dependent JAR
152          files (if any). Place all files together in the same directory. For more
153          information read
154          <xref linkend="plugins.installation" />
155        </para>
156      </sect3>
157    </sect2>
158   
159    <sect2 id="plugin_developer.organize.eclipse">
160      <title>With Eclipse</title>
161      <para>TODO</para>
162    </sect2>
163   
164  </sect1>
165
166  <sect1 id="plugin_developer.api">
167    <title>The Plug-in API</title>
168    <para>
169    </para>
170   
171    <sect2 id="plugin_developer.api.interfaces">
172   
173      <title>The main plug-in interfaces</title>
174      <para>
175        The Base2 core defines two interfaces and one
176        abstract class that are vital for implementing plug-ins:
177        <itemizedlist spacing="compact">
178          <listitem>
179            <simpara>
180              <interfacename>net.sf.basedb.core.plugin.Plugin</interfacename>
181            </simpara>
182          </listitem>
183          <listitem>
184            <simpara>
185              <interfacename>net.sf.basedb.core.plugin.InteractivePlugin</interfacename>
186            </simpara>
187          </listitem>
188          <listitem>
189            <simpara>
190              <classname>net.sf.basedb.core.plugin.AbstractPlugin</classname>
191            </simpara>
192          </listitem>
193        </itemizedlist>
194       
195        A plug-in is always required to implement the
196        <interfacename>Plugin</interfacename> interface.
197        The <interfacename>InteractivePlugin</interfacename>
198        interface is optional, and is only needed if you want user interaction.
199        The <classname>AbstractPlugin</classname> is a useful base class
200        that your plug-in can use as a superclass. It provides default implementations
201        for some of the interface methods and also has utility methods for
202        validating and storing job and configuration parameter values. Another
203        reason to use this class as a superclass is that it will shield your
204        plug-in from future changes to the Plug-in API. For example, if
205        we decide that a new method is needed in the <interfacename>Plugin</interfacename> 
206        interface we will also try to add a default implementation in
207        the <classname>AbstractPlugin</classname> class.
208      </para>
209 
210      <important>
211        <para>
212        A plug-in must also have public no-argument contructor. Otherwise, BASE will not
213        be able to create instances of the class.
214        </para>
215      </important>
216     
217      <sect3 id="plugin_developer.api.interfaces.plugin">
218        <title>net.sf.basedb.core.plugin.Plugin</title>
219        <para>This interface defines seven methods and must be implemented by all plug-ins.</para>
220        <variablelist>
221          <varlistentry>
222            <term>
223              <methodsynopsis language="java">
224                <modifier>public</modifier>
225                <type>About</type>
226                <methodname>getAbout</methodname>
227                <void />
228              </methodsynopsis>
229            </term>
230            <listitem>
231              <para>
232                Return information about the plug-in, i.e. the name, version, and a short
233                description about what the plug-in does. The
234                <classname>About</classname>
235                object also has fields for naming the author and various other contact
236                information. The returned information is copied by the core at
237                installation time into the database. The only required information is
238                the name of the plug-in. All other fields may have null values.
239              </para>
240              <example id="net.sf.basedb.core.plugin.Plugin.getAbout">
241                <title>A typical implementation stores this information in a static field</title>
242                <programlisting>
243private static final About about = new AboutImpl
244(
245   "Spot images creator",
246   "Converts a full-size scanned image into smaller preview jpg " +
247   "images for each individual spot.",
248   "2.0",
249   "2006, Department of Theoretical Physics, Lund University",
250   null,
251   "base@thep.lu.se",
252   "http://base.thep.lu.se"
253);
254 
255public About getAbout()
256{
257   return about;
258}
259</programlisting>
260              </example>
261            </listitem>
262          </varlistentry>
263          <varlistentry>
264            <term>
265              <methodsynopsis language="java">
266                <modifier>public</modifier>
267                <type>Plugin.MainType</type>
268                <methodname>getMainType</methodname>
269                <void />
270              </methodsynopsis>
271            </term>
272            <listitem>
273              <para>
274                Return information about the main type of plug-in. The
275                <classname>MainType</classname>
276                is an enumeration with five possible values:
277                <itemizedlist>
278                  <listitem>
279                    <para>
280                      <constant>ANALYZE</constant>:
281                      An analysis plug-in
282                    </para>
283                  </listitem>
284                  <listitem>
285                    <para>
286                      <constant>EXPORT</constant>:
287                      A plug-in the exports data
288                    </para>
289                  </listitem>
290                  <listitem>
291                    <para>
292                      <constant>IMPORT</constant>:
293                      A plug-in that imports data
294                    </para>
295                  </listitem>
296                  <listitem>
297                    <para>
298                      <constant>INTENSITY</constant>:
299                      A plug-in that calculates the original spot intensities
300                      from raw data
301                    </para>
302                  </listitem>
303                  <listitem>
304                    <para>
305                      <constant>OTHER</constant>:
306                      Any other type of plug-in
307                    </para>
308                  </listitem>
309                </itemizedlist>
310                The returned value is stored in the database but is otherwise not used
311                by the core. Client applications (such as the web client) will probably
312                use this information to group the plug-ins, i.e., a button labeled
313                <guibutton>Export</guibutton>
314                will let you select among the export plug-ins.
315              </para>
316              <example id="net.sf.basedb.core.plugin.Plugin.getMainType">
317                <title>A typical implementation just return one of the values</title>
318                <programlisting>
319public Plugin.MainType getMainType()
320{
321   return Plugin.MainType.OTHER;
322}
323</programlisting>
324              </example>
325            </listitem>
326          </varlistentry>
327          <varlistentry>
328            <term>
329              <methodsynopsis language="java">
330                <modifier>public</modifier>
331                <type>boolean</type>
332                <methodname>supportsConfigurations</methodname>
333                <void />
334              </methodsynopsis>
335            </term>
336            <listitem>
337              <para>
338                If this method returns true the plug-in can have different
339                configurations, (i.e.
340                <classname>PluginConfiguration</classname>).
341                Note that this method may return true even if the
342                <interfacename>InteractivePlugin</interfacename>
343                interface isn't implemented. The
344                <classname>AbstractPlugin</classname>
345                returns true for this method which is the old way before the
346                introduction of this method.
347              </para>
348            </listitem>
349          </varlistentry>
350          <varlistentry>
351            <term>
352              <methodsynopsis language="java">
353                <modifier>public</modifier>
354                <type>boolean</type>
355                <methodname>requiresConfiguration</methodname>
356                <void />
357              </methodsynopsis>
358            </term>
359            <listitem>
360              <para>
361                If this method returns true the plug-in must have a configuration
362                to be able to run. For example, some of the core import plug-ins
363                must have information about the file format to be able to import
364                any data.
365                The
366                <classname>AbstractPlugin</classname>
367                returns false for this method which is the old way before the
368                introduction of this method.
369              </para>
370            </listitem>
371          </varlistentry>
372          <varlistentry>
373            <term>
374              <methodsynopsis language="java">
375                <modifier>public</modifier>
376                <void />
377                <methodname>init</methodname>
378                <methodparam>
379                  <type>SessionControl</type>
380                  <parameter>sc</parameter>
381                </methodparam>
382                <methodparam>
383                  <type>ParameterValues</type>
384                  <parameter>configuration</parameter>
385                </methodparam>
386                <methodparam>
387                  <type>ParameterValues</type>
388                  <parameter>job</parameter>
389                </methodparam>
390                <exceptionname>BaseException</exceptionname>
391              </methodsynopsis>
392            </term>
393            <listitem>
394              <para>
395                Prepare the plug-in for execution (or configuration). If the plug-in needs
396                to do some initialization this is the place to do it. A typical
397                implementation however only stores the passed parameters in instance
398                variables for later use.
399              </para>
400              <para>
401                The parameters passed to this method has vital information that is
402                needed to execute the plug-in. The
403                <classname>SessionControl</classname>
404                is a central core object which holds information about the logged in
405                user and are used to create
406                <classname>DbControl</classname>
407                objects which allows a plug-in to connect to the database to read, add or
408                update information. The two
409                <classname>ParameterValues</classname>
410                objects contains information about the configuration and job
411                parameters to the plug-in.
412                The <varname>configuration</varname> object holds all parameters stored
413                together with a <classname>PluginConfiguration</classname>
414                object in the database. If the plug-in is started without
415                a configuration this object is null.
416                The <varname>job</varname> object holds all parameters that are
417                stored together with a <classname>Jon</classname> object in the
418                database. This object is null if the plug-in is started without a job.
419              </para>
420              <para>
421                The difference between a configuration parameter and a job parameter is
422                that a configuration is usually something an administrator sets up,
423                while a job is an actual execution of a plug-in. For example, a
424                configuration for an import plug-in holds the regular expressions needed
425                to parse a text file and find the headers, sections and data lines,
426                while the job holds the file to parse.
427              </para>
428              <para>
429                The
430                <classname>AbstractPlugin</classname>
431                contains an implementation of this method that saves the passed objects
432                in protected instance variables. If you override this method
433                we recommend that you also call <code>super.init()</code>.
434              </para>
435              <example id="net.sf.basedb.core.plugin.Plugin.init">
436                <title>The <classname>AbstractPlugin</classname> implementation of this Plugin.init()</title>
437                <programlisting>
438protected SessionControl sc = null;
439protected ParameterValues configuration = null;
440protected ParameterValues job = null;
441/**
442   Store copies of the session control, plug-in and job configuration. These
443   are available to subclasses in the {@link #sc}, {@link #configuration}
444   and {@link #job} variables. If a subclass overrides this method it is
445   recommended that it also calls super.init(sc, configuration, job).
446*/
447public void init(SessionControl sc,
448   ParameterValues configuration, ParameterValues job)
449   throws BaseException
450{
451   this.sc = sc;
452   this.configuration = configuration;
453   this.job = job;
454}
455</programlisting>
456              </example>
457            </listitem>
458          </varlistentry>
459          <varlistentry>
460            <term>
461              <methodsynopsis language="java">
462                <modifier>public</modifier>
463                <void />
464                <methodname>run</methodname>
465                <methodparam>
466                  <type>Request</type>
467                  <parameter>request</parameter>
468                </methodparam>
469                <methodparam>
470                  <type>Response</type>
471                  <parameter>response</parameter>
472                </methodparam>
473                <methodparam>
474                  <type>ProgressReporter</type>
475                  <parameter>progress</parameter>
476                </methodparam>
477              </methodsynopsis>
478            </term>
479            <listitem>
480              <para>
481                Runs the plug-in. The <varname>request</varname>
482                parameter is of historical interest only. It has no useful information
483                and can be ignored.
484              </para>
485              <para>
486                The <varname>progress</varname> parameter
487                can be used by a plug-in to report it's progress back to the core. The
488                core will usually send the progress information to the database, which
489                allows users to see exactly how the plug-in is progressing from the web
490                interface. This parameter can be null, but if it isn't we recommend all
491                plug-ins to use it. However, it should be used sparingly, since each call
492                to set the progress results in a database update. If the execution
493                involves several thousands of items it is a bad idea to update the
494                progress after processing each one of them. A good starting point is to
495                divide the work into 100 pieces each representing 1% of the work, i.e.,
496                if the plug-in should export 100 000 items it should report progress
497                after every 1000 items.
498              </para>
499              <para>
500                The <varname>response</varname>
501                parameter is used to tell the core if the plug-in was successful or
502                failed. Not setting a response is considered a failure by the core. From
503                the run method it is only allowed to use the
504                <methodname>setDone()</methodname>
505                or the
506                <methodname>setError()</methodname>
507                methods.
508              </para>
509             
510              <note>
511                It is also considered bad practice to let exceptions escape
512                out from this method. Always use <code>try...catch</code>
513                to catch exceptions and use <code>response.setError()</code>
514                to reporter the error back to the core.
515              </note>
516             
517              <example id="net.sf.basedb.core.plugin.Plugin.run">
518                <title>
519                  Here is a skeleton that we recommend each plug-in to use in it's
520                  implementation of the
521                  <methodname>run()</methodname>
522                  method
523                </title>
524                <programlisting>
525public void run(Request request, Response response, ProgressReporter progress)
526{
527   // Open a connection to the database
528   // sc is set by init() method
529   DbControl dc = sc.newDbControl();
530   try
531   {
532      // Insert code for plug-in here
533
534      // Commit the work
535      dc.commit();
536      response.setDone("Plug-in ended successfully");
537   }
538   catch (Throwable t)
539   {
540      // All exceptions must be catched and sent back
541      // using the response object
542      response.setError(t.getMessage(), Arrays.asList(t));
543   }
544   finally
545   {
546      // IMPORTANT!!! Make sure opened connections are closed
547      if (dc != null) dc.close();
548   }
549}
550</programlisting>
551              </example>
552            </listitem>
553          </varlistentry>
554          <varlistentry>
555            <term>
556              <methodsynopsis language="java">
557                <modifier>public</modifier>
558                <void />
559                <methodname>done</methodname>
560                <void />
561              </methodsynopsis>
562            </term>
563            <listitem>
564              <para>
565                Clean up all resources after executing the plug-in. This method mustn't
566                throw any exceptions.
567              </para>
568              <example id="net.sf.basedb.core.plugin.Plugin.done">
569                <title>
570                  The
571                  <classname>AbstractPlugin</classname>
572                  contains an implementation of the <methodname>done()</methodname>
573                  method simply sets the
574                  parameters passed to the
575                  <methodname>init()</methodname>
576                  method to null
577                </title>
578                <programlisting>
579/**
580   Clears the variables set by the init method. If a subclass
581   overrides this method it is recommended that it also calls super.done().
582*/
583public void done()
584{
585   configuration = null;
586   job = null;
587   sc = null;
588}
589                </programlisting>
590              </example>
591            </listitem>
592          </varlistentry>
593        </variablelist>
594      </sect3>
595 
596      <sect3 id="plugin_developer.api.interfaces.interactive">
597        <title>net.sf.basedb.core.plugin.InteractivePlugin</title>
598        <para>
599          If you want the plug-in to be able to interact with the user you must also implement
600          this interface. This is probably the case for most plug-ins. Among the core plug-ins
601          shipped with BASE the
602          <classname>SpotImageCreator</classname>
603          is one plug-in that doesn't interact with the user. Instead, the web client has
604          special JSP pages that handles all the interaction, creates a job for it and sets
605          the parameters. This, kind of hardcoded, approach can be used for other plug-ins as
606          well, but then it usually requires modification of the client application as well.
607        </para>
608        <para>
609          The
610          <interfacename>InteractivePlugin</interfacename>
611          has three main tasks:
612         
613          <orderedlist>
614          <listitem>
615            <para>
616            Tell a client application where the plug-in should be plugged
617            in.
618            </para>
619          </listitem>
620          <listitem>
621            <para>
622            Ask the users for configuration and job parameters.
623            </para>
624          </listitem>
625         
626          <listitem>
627            <para>
628            Validate parameter values entered by the user and store those in the
629            database.
630            </para>
631          </listitem>
632          </orderedlist>
633          This requires that the following methods are
634          implemented.
635        </para>
636       
637        <variablelist>
638          <varlistentry>
639            <term>
640              <methodsynopsis language="java">
641                <modifier>public</modifier>
642                <type>Set&lt;GuiContext&gt;</type>
643                <methodname>getGuiContexts</methodname>
644                <void />
645              </methodsynopsis>
646            </term>
647            <listitem>
648              <para>
649                Return information about where the plug-in should be plugged in. Each
650                place is identified by a
651                <classname>GuiContext</classname>
652                object, which is an
653                <classname>Item</classname>
654                and a
655                <classname>Type</classname>.
656                The item is one of the objects defined by the
657                <classname>net.sf.basedb.core.Item</classname>
658                enumeration and the type is either
659                <constant>Type.LIST</constant>
660                or
661                <constant>Type.ITEM</constant>, which corresponde to the
662                list view and the single-item view in the web client.
663              </para>
664              <para>
665                For example, the
666                <varname>GuiContext</varname> =
667                (<constant>Item.REPORTER</constant>,
668                <constant>Type.LIST</constant>)
669                tells a client application that this plug-in can be plugged in whenever a
670                list of reporters is displayed. The
671                <varname>GuiContext</varname> =
672                (<constant>Item.REPORTER</constant>,
673                <constant>Type.ITEM</constant>)
674                tells a client application that this plug-in can be plugged in whenever
675                a single reporter is displayed. The first case may be appropriate for a
676                plug-in that imports or exports reporters. The second case may be used by
677                a plug-in that updates the reporter information from an external source
678                (well, it may make sense to use this in the list case as well).
679              </para>
680              <para>
681                The returned information is copied by the core at installation time to
682                make it easy to ask for all plug-ins for a certain
683                <classname>GuiContext</classname>.
684              </para>
685              <para>
686                A typical implementation creates a static unmodifiable
687                <classname>Set</classname>
688                which is returned by this method. It is important that the returned set
689                can't be modified. It may be a security issue if a misbehaving
690                client application does that.
691              </para>
692              <example id="net.sf.basedb.core.plugin.InteractivePlugin.getGuiContexts">
693                <title>
694                  A typical implementation of
695                  <methodname>getGuiContexts</methodname>
696                </title>
697                <programlisting>
698// From the net.sf.basedb.plugins.RawDataFlatFileImporter plug-in
699private static final Set&lt;GuiContext&gt; guiContexts =
700   Collections.singleton(new GuiContext(Item.RAWBIOASSAY, GuiContext.Type.ITEM));
701
702public Set&lt;GuiContext&gt; <methodname>getGuiContexts</methodname>()
703{
704   return <returnvalue>guiContexts</returnvalue>;
705}
706</programlisting>
707              </example>
708            </listitem>
709          </varlistentry>
710          <varlistentry>
711            <term>
712              <methodsynopsis language="java">
713                <modifier>public</modifier>
714                <type>String</type>
715                <methodname>isInContext</methodname>
716                <methodparam>
717                  <type>GuiContext</type>
718                  <parameter>context</parameter>
719                </methodparam>
720                <methodparam>
721                  <type>Object</type>
722                  <parameter>item</parameter>
723                </methodparam>
724              </methodsynopsis>
725            </term>
726            <listitem>
727              <para>
728                This method is called to check if a particular item is usable for the
729                plug-in. This method is only invoked from the single-item view.
730                Thus, <code>context.getType()</code> always returns
731                <constant>Type.ITEM</constant>, and <code>context.getItem()</code>
732                returns a value corresponding to the type of item that is passed
733                in the <varname>item</varname> parameter. Here is an example:
734               
735                <informalexample>
736                <para>
737                The user has selected a specific sample and the the client
738                application is now displaying information about that sample.
739                Thus, our
740                <varname>GuiContext</varname> =
741                (<constant>Item.SAMPLE</constant>,
742                <constant>Type.ITEM</constant>).
743               
744                Now, the client application asks for a list of plug-ins supporting
745                this context and for each one in the list calls this method with the
746                current sample as the value of the <varname>item</varname> parameter.
747                </para>
748                </informalexample>
749
750                If the plug-in can do whatever it is supposed to do it should
751                return null, otherwise it should return a string with a message
752                explaining why it can't.
753              </para>
754              <para>
755                Here is a real example from the
756                <classname>RawDataFlatFileImporter</classname>
757                plug-in which imports raw data to a
758                <classname>RawBioAssay</classname>.
759               
760                Thus,
761                <varname>GuiContext</varname> =
762                (<constant>Item.RAWBIOASSAY</constant>,
763                <constant>Type.ITEM</constant>),
764               
765                but the plug-in can only import data if there isn't any already, and
766                if the raw bioassay has the same raw data type as the plug-in has been
767                configured for.
768              </para>
769              <example id="net.sf.basedb.core.plugin.InteractivePlugin.isInContext">
770                <title>
771                  A simple implementation of
772                  <methodname>isInContext</methodname>
773                </title>
774                <programlisting>
775/**
776   Returns null if the item is a {@link RawBioAssay} of the correct
777   {@link RawDataType} and doesn't already have spots.
778*/
779public String isInContext(GuiContext context, Object item)
780{
781   String message = null;
782   if (item == null)
783   {
784      message = "The object is null";
785   }
786   else if (!(item instanceof RawBioAssay))
787   {
788      message = "The object is not a RawBioAssay: " + item;
789   }
790   else
791   {
792      RawBioAssay rba = (RawBioAssay)item;
793      String rawDataType = (String)configuration.getValue("rawDataType");
794      if (rba.getSpots() > 0)
795      {
796         message = "The raw bioassay already has spots: " + rba.getName();
797      }
798      else if (!rba.getRawDataType().getId().equals(rawDataType))
799      {
800         message = "Unsupported raw data type: " + rba.getRawDataType().getName();
801      }
802   }
803   return message;   
804}
805</programlisting>
806              </example>
807            </listitem>
808          </varlistentry>
809          <varlistentry>
810            <term>
811              <methodsynopsis language="java">
812                <modifier>public</modifier>
813                <type>RequestInformation</type>
814                <methodname>getRequestInformation</methodname>
815                <methodparam>
816                  <type>GuiContext</type>
817                  <parameter>context</parameter>
818                </methodparam>
819                <methodparam>
820                  <type>String</type>
821                  <parameter>command</parameter>
822                </methodparam>
823                <exceptionname>BaseException</exceptionname>
824              </methodsynopsis>
825            </term>
826            <listitem>
827              <para>
828                Ask the plug-in for parameters that needs to be entered by the user. The
829                <classname>GuiContext</classname>
830                parameter is one of the contexts returned by the
831                <methodname>getGuiContexts</methodname>
832                method. The command is string telling the plug-in what command was
833                executed. There are two predefined commands but as you will see the
834                plug-in may define it's own commands. The two predefined commands are
835                defined in the
836                <classname>net.sf.basedb.core.plugin.Request</classname>
837                class.
838                <variablelist>
839                  <varlistentry>
840                    <term>
841                      <constant>Request.COMMAND_CONFIGURE_PLUGIN</constant>
842                    </term>
843                    <listitem>
844                      <para>
845                        Used when an administrator is initiating a configuration
846                        of the plug-in.
847                      </para>
848                    </listitem>
849                  </varlistentry>
850                  <varlistentry>
851                    <term>
852                      <constant>Request.COMMAND_CONFIGURE_JOB</constant>
853                    </term>
854                    <listitem>
855                      <para>
856                        Used when a user has selected the plug-in for running a
857                        job.
858                      </para>
859                    </listitem>
860                  </varlistentry>
861                </variablelist>
862                Given this information the plug-in must return a
863                <classname>RequestInformation</classname>
864                object. This is simply a title, a description and a list of parameters.
865                Usually the title will end up as the input form title and the
866                description as a help text for the entire form. Do not put information
867                about the individual parameters in this description, since each
868                parameter has a description of their own.
869              </para>
870              <example id="net.sf.basedb.core.plugin.InteractivePlugin.getRequestInformation">
871                <title>
872                  When running an import plug-in it needs to ask for the file to import
873                  from and if existing items should be updated or not
874                </title>
875                <programlisting >
876// The complete request information
877private RequestInformation configure Job;
878
879// The parameter that asks for a file to import from
880private PluginParameter&lt;File&gt; file Parameter;
881
882// The parameter that asks if existing items should be updated or not
883private PluginParameter&lt;Boolean&gt; updateExistingParameter;
884
885public RequestInformation getRequestInformation(GuiContext context, String command)
886   throws BaseException
887{
888   RequestInformation requestInformation = null;
889   if (command.equals(Request.COMMAND_CONFIGURE_PLUGIN))
890   {
891      requestInformation = getConfigurePlugin();
892   }
893   else if (command.equals(Request.COMMAND_CONFIGURE_JOB))
894   {
895      requestInformation = getConfigureJob();
896   }
897   return requestInformation;
898}
899
900/**
901   Get (and build) the request information for starting a job.
902*/
903private RequestInformation getConfigureJob()
904{
905   if (configureJob == null)
906   {
907      fileParameter = new PluginParameter&lt;File&gt;(
908         "file",
909         "File",
910         "The file to import the data from",
911         new FileParameterType(null, true, 1)
912      );
913     
914      updateExistingParameter = new PluginParameter&lt;Boolean&gt;(
915         "updateExisting",
916         "Update existing items",
917         "If this option is selected, already existing items will be updated " +
918         " with the information in the file. If this option isn't selected " +
919         " existing items are left untouched.",
920         new BooleanParameterType(false, true)
921      );
922
923      List&lt;PluginParameter&lt;?&gt;&gt; parameters =
924         new ArrayList&lt;PluginParameter&lt;?&gt;&gt;(2);
925      parameters.add(fileParameter);
926      parameters.add(updateExistingParameter);
927     
928      configureJob = new RequestInformation
929      (
930         Request.COMMAND_CONFIGURE_JOB,
931         "Select a file to import items from",
932         "Description",
933         parameters
934      );
935   }
936   return configureJob;
937}
938</programlisting>
939              </example>
940              <para>
941                As you can see it takes a lot of code to put together a
942                <classname>RequestInformation</classname>
943                object. For each parameter you need one
944                <classname>PluginParameter</classname>
945                object and one
946                <classname>ParameterType</classname>
947                object. To make life a little easier, a
948                <classname>ParameterType</classname>
949                can be reused for more than one
950                <classname>PluginParameter</classname>.
951              </para>
952             
953              <programlisting>
954StringParameterType stringPT = new StringParameterType(255, null, true);
955PluginParameter one = new PluginParameter("one", "One", "First string", stringPT);
956PluginParameter two = new PluginParameter("two", "Two", "Second string", stringPT);
957// ... and so on
958</programlisting>
959              <para>
960                The
961                <classname>ParameterType</classname>
962                is an abstract base class for several subclasses each implementing a
963                specific type of parameter. The list of subclasses may grow in the
964                future, but here are the most important ones currently implemented.
965              </para>
966              <note>
967                <para>
968                  Most parameter types include support for supplying a predefined list
969                  of options to select from. In that case the list will be displayed
970                  as a drop-down list for the user, otherwise a free input field is
971                  used.
972                </para>
973              </note>
974              <variablelist>
975                <varlistentry>
976                  <term>
977                    <classname>StringParameterType</classname>
978                  </term>
979                  <listitem>
980                    <para>
981                      Asks for a string value. Includes an option for
982                      specifying the maximum length of the string.
983                    </para>
984                  </listitem>
985                </varlistentry>
986                <varlistentry>
987                  <term>
988                    <classname>FloatParameterType</classname>,
989                    <classname>DoubleParameterType</classname>,
990                    <classname>IntegerParameterType</classname>,
991                    <classname>LongParameterType</classname>
992                  </term>
993                  <listitem>
994                    <para>
995                      Asks for numerical values. Includes options for
996                      specifying a range (min/max) of allowed values.
997                    </para>
998                  </listitem>
999                </varlistentry>
1000                <varlistentry>
1001                  <term>
1002                    <classname>BooleanParameterType</classname>
1003                  </term>
1004                  <listitem>
1005                    <para>Asks for a boolean value.
1006                    </para>
1007                  </listitem>
1008                </varlistentry>
1009                <varlistentry>
1010                  <term>
1011                    <classname>DateParameterType</classname>
1012                  </term>
1013                  <listitem>
1014                    <para>Asks for a date.
1015                    </para>
1016                  </listitem>
1017                </varlistentry>
1018                <varlistentry>
1019                  <term>
1020                    <classname>FileParameterType</classname>
1021                  </term>
1022                  <listitem>
1023                    <para>Asks for a file item.
1024                    </para>
1025                  </listitem>
1026                </varlistentry>
1027                <varlistentry>
1028                  <term>
1029                    <classname>ItemParameterType</classname>
1030                  </term>
1031                  <listitem>
1032                    <para>
1033                      Asks for any other item. This parameter type requires
1034                      that a list of options is supplied, except when the item
1035                      type asked for matches the current <classname>GuiContext</classname>, in which
1036                      case the currently selected item is used as the
1037                      parameter value.
1038                    </para>
1039                  </listitem>
1040                </varlistentry>
1041                <varlistentry>
1042                  <term>
1043                    <classname>PathParameterType</classname>
1044                  </term>
1045                  <listitem>
1046                    <para>
1047                      Ask for a path to a file or directory. The path may be
1048                      non-existing and should be used when a plug-in needs an
1049                      output destination, i.e., the file to export to, or a
1050                      directory where the output files should be placed.
1051                    </para>
1052                  </listitem>
1053                </varlistentry>
1054              </variablelist>
1055              <para>
1056                You can also create a
1057                <classname>PluginParameter</classname>
1058                with a null name and
1059                <classname>ParameterType</classname>.
1060                In this case, the web client will not ask for input from the user, instead
1061                it is used as a section header, allowing you to group parameters into
1062                different sections which increase the readability of the input
1063                parameters page.
1064              </para>
1065              <programlisting>
1066PluginParameter firstSection = new PluginParameter(null, "First section", null, null);
1067PluginParameter secondSection = new PluginParameter(null, "Second section", null, null);
1068// ...
1069
1070parameters.add(firstSection);
1071parameters.add(firstParameterInFirstSection);
1072parameters.add(secondParameteInFirstSection);
1073
1074parameters.add(secondSection);
1075parameters.add(firstParameterInSecondSection);
1076parameters.add(secondParameteInSecondSection);
1077</programlisting>
1078            </listitem>
1079          </varlistentry>
1080          <varlistentry>
1081            <term>
1082              <methodsynopsis language="java">
1083                <modifier>public</modifier>
1084                <void />
1085                <methodname>configure</methodname>
1086                <methodparam>
1087                  <type>GuiContext</type>
1088                  <parameter>context</parameter>
1089                </methodparam>
1090                <methodparam>
1091                  <type>Request</type>
1092                  <parameter>request</parameter>
1093                </methodparam>
1094                <methodparam>
1095                  <type>Response</type>
1096                  <parameter>response</parameter>
1097                </methodparam>
1098              </methodsynopsis>
1099            </term>
1100            <listitem>
1101              <para>
1102                Sends parameter values entered by the user for processing by the plug-in.
1103                The plug-in must validate that the parameter values are
1104                correct and then store them in database.
1105              </para>
1106             
1107              <important>
1108                <para>
1109                No validation is done by the core, except converting the input to the
1110                correct object type, i.e. if the plug-in asked for a
1111                <classname>Float</classname>
1112                the input string is parsed and converted to a
1113                <classname>Float</classname>. If you have extended the
1114                <classname>AbstractPlugin</classname>
1115                class it is very easy to validate the parameters with the
1116                <methodname>validateRequestParameters()</methodname>
1117                method. This method takes the same list of
1118                <classname>PluginParameter</classname>:s
1119                as used in the
1120                <classname>RequestInformation</classname>
1121                object and uses that information for validation. It returns null or a
1122                list of
1123                <exceptionname>Throwable</exceptionname>:s that can be
1124                given directly to the <code>response.setError()</code>
1125                methods.
1126                </para>
1127              </important>
1128              <para>
1129                When the parameters have been validated they need to be stored
1130                in the datatbase. Once again, it is very easy if you use one of the
1131                <methodname>AbstractPlugin.storeValue()</methodname>
1132                or
1133                <methodname>AbstractPlugin.storeValues()</methodname>
1134                methods.
1135              </para>
1136              <para>
1137                The configure method works much like the <methodname>Plugin.run()</methodname> 
1138                method. It must return the result in the <classname>Response</classname> object,
1139                and shouldn't trow any exceptions.
1140              </para>
1141             
1142              <example id="net.sf.basedb.core.plugin.InteractivePlugin.configure">
1143                <title>
1144                  Configuration implementation building on the examples above
1145                </title>
1146                <programlisting>
1147public void configure(GuiContext context, Request request, Response response)
1148{
1149   String command = request.getCommand();
1150   try
1151   {
1152      if (command.equals(Request.COMMAND_CONFIGURE_PLUGIN))
1153      {
1154         // TODO
1155      }
1156      else if (command.equals(Request.COMMAND_CONFIGURE_JOB))
1157      {
1158         // Validate user input
1159         List&lt;Throwable&gt; errors =
1160            validateRequestParameters(getConfigureJob().getParameters(), request);
1161         if (errors != null)
1162         {
1163            response.setError(errors.size() +
1164               " invalid parameter(s) were found in the request", errors);
1165            return;
1166         }
1167         
1168         // Store user input
1169         storeValue(job, request, fileParameter);
1170         storeValue(job, request, updateExistingParameter);
1171         
1172         // We are happy and done
1173         response.setDone("Job configuration complete", Job.ExecutionTime.SHORT);
1174         // TODO - check file size to make a better estimate of execution time
1175      }
1176   }
1177   catch (Throwable ex)
1178   {
1179      response.setError(ex.getMessage(), Arrays.asList(ex));
1180   }
1181}
1182</programlisting>
1183              </example>
1184             
1185              <para>
1186                Note that the call to
1187                <methodname>response.setDone()</methodname>
1188                has a second parameter
1189                <constant>Job.ExecutionTime.SHORT</constant>. It is an indication
1190                about how long time it will take to execute the
1191                plug-in. This is of interest for job queue managers which probably
1192                doesn't want to start too many long-running jobs at the same time
1193                blocking the entire system. Please try to use this parameter wisely and
1194                not use <constant>Job.ExecutionTime.SHORT</constant>
1195                out of old habit all the time.
1196              </para>
1197              <para>
1198                The <classname>Response</classname> class also has a
1199                <methodname>setContinue()</methodname>
1200                method which tells the core that the plug-in needs more parameters,
1201                i.e. the core will then call
1202                <methodname>getRequestInformation()</methodname>
1203                again with the new command, let the user enter values, and then call
1204                <methodname>configure()</methodname>
1205                with the new values. This process is repeated until the plug-in
1206                reports that it is done or an error occurs.
1207              </para>
1208              <para>
1209                An important note is that during this iteration it is the same instance
1210                of the plug-in that is used. However, no parameter values are stored in
1211                the database until the plugin sends a
1212                <methodname>response.setDone()</methodname>.
1213                After that, the plug-in instance is usually discarded, and a job is placed
1214                in the job queue. The execution of the plug-in happens in a new instance
1215                and maybe on a different server.
1216              </para>
1217              <tip>
1218                  <para>
1219                    You don't have to store all values the plug-in asked for in the
1220                    first place. You may even choose to store different values than
1221                    those that were entered. For example, you might ask for the mass
1222                    and height of a person and then only store the body mass index,
1223                    which is calculated from those values.
1224                  </para>
1225              </tip>
1226            </listitem>
1227          </varlistentry>
1228        </variablelist>
1229      </sect3>
1230   
1231    </sect2>
1232 
1233    <sect2 id="plugin_developer.api.callsequence">
1234      <title>What happens when...</title>
1235     
1236      <para>
1237        This section describes how the BASE core interacts with the
1238        plug-in in a number of use cases. We will outline the
1239        order the methods are invoked on the plug-in.
1240      </para>
1241     
1242      <sect3 id="plugin_developer.api.callsequence.install">
1243        <title>Installing a plug-in</title>
1244       
1245        <para>
1246          When a plug-in is installed the core is eager to find out
1247          information about the plug-in. To do this it calls the
1248          following methods in this order:
1249        </para>
1250       
1251        <orderedlist>
1252        <listitem>
1253          <para>
1254          A new instance of the plug-in class is created. The plug-in must
1255          have a public no-argument constructor.
1256          </para>
1257        </listitem>
1258       
1259        <listitem>
1260          <para>
1261          Calls are made to <methodname>Plugin.getMainType()</methodname>,
1262          <methodname>Plugin.supportsConfigurations()</methodname>,
1263          <methodname>Plugin.requiresConfiguration()</methodname> and
1264          <methodname>Plugin.getAbout()</methodname> to find out information
1265          about the plug-in. This is the only time theese methods are called.
1266          The information that is returned by them are copied and stored in
1267          the database for easy access.
1268          </para>
1269         
1270          <note>
1271            <para>
1272            The <methodname>Plugin.init()</methodname> method is
1273            never called during plug-in installation.
1274            </para>
1275          </note>
1276        </listitem>
1277       
1278        <listitem>
1279          <para>
1280          If the plug-in implements the <interfacename>InteractivePlugin</interfacename>
1281          interface the <methodname>InteractivePlugin.getGuiContexts()</methodname>
1282          method is called. This is the only time this method. The information it
1283          returns are copied and stored in the database.
1284          </para>
1285        </listitem>
1286       
1287        <listitem>
1288          <para>
1289          If the server admin decided to use the plug-in permission system, the
1290          <methodname>Plugin.getPermissions()</methodname> method is called.
1291          The returned information is copied and stored in the database.
1292          </para>
1293        </listitem>
1294       
1295        </orderedlist>
1296      </sect3>
1297   
1298      <sect3 id="plugin_developer.api.callsequence.configure">
1299        <title>Configuring a plug-in</title>
1300       
1301        <para>
1302          The plug-in must implement the <interfacename>InteractivePlugin</interfacename>
1303          interface and the <methodname>Plugin.supportsConfigurations()</methodname> method
1304          must return <constant>TRUE</constant>. The configuration is done with
1305          a wizard-like interface (see <xref linkend="plugins.configuration.wizard" />).
1306          The same plug-in instance is used throughout the entire configuration sequence.
1307        </para>
1308       
1309        <orderedlist>
1310        <listitem>
1311          <para>
1312          A new instance of the plug-in class is created. The plug-in must
1313          have a public no-argument constructor.
1314          </para>
1315        </listitem>
1316       
1317        <listitem>
1318          <para>
1319          The <methodname>Plugin.init()</methodname> method is called. The
1320          <varname>job</varname> parameter is null.
1321          </para>
1322        </listitem>
1323       
1324        <listitem id="plugin_developer.api.callsequence.configure.requestinformation">
1325          <para>
1326          The <methodname>InteractivePlugin.getRequestInformation()</methodname> 
1327          method is called. The <varname>context</varname> parameter is <constant>null</constant>
1328          and the <varname>command</varname> is the value of the string
1329          constant <constant>Request.COMMAND_CONFIGURE_PLUGIN</constant> 
1330          (_config_plugin).
1331          </para>
1332        </listitem>
1333       
1334        <listitem id="plugin_developer.api.callsequence.configure.form">
1335          <para>
1336          The web client process this information and displays a form for user
1337          input. The plug-in will have to wait some time while the user enters
1338          data.
1339          </para>
1340        </listitem>
1341       
1342        <listitem>
1343          <para>
1344          The <methodname>InteractivePlugin.configure()</methodname> method
1345          is called. The <varname>context</varname> parameter is still
1346          <constant>null</constant> and the <varname>request</varname>
1347          parameter contains the parameter values entered by the user.
1348          </para>
1349        </listitem>
1350       
1351        <listitem>
1352          <para>
1353          The plug-in must validate the values and decided if they should be
1354          stored in the database or not. We recommend that you use the
1355          methods in the <classname>AbstractPlugin</classname> class for this.
1356          </para>
1357        </listitem>
1358       
1359        <listitem>
1360          <para>
1361          The plug-in has three different respones:
1362         
1363          <itemizedlist>
1364          <listitem>
1365            <para>
1366            <methodname>Response.setDone()</methodname>: The configuration
1367            is complete. The core will write any configuation changes to the
1368            database, call the <methodname>Plugin.done()</methodname> method and
1369            then discard the plug-in instance.
1370            </para>
1371          </listitem>
1372         
1373          <listitem>
1374            <para>
1375            <methodname>Response.setError()</methodname>: There was one or more
1376            errors. The web client will display the error messages for the user and
1377            allow the user to enter new values. The process continues with
1378            step <xref linkend="plugin_developer.api.callsequence.configure.form" />.
1379            </para>
1380          </listitem>
1381         
1382          <listitem>
1383            <para>
1384            <methodname>Response.setContinue()</methodname>: The parameters are correct
1385            but the plug-in wants more parameters. The process continues with
1386            step <xref linkend="plugin_developer.api.callsequence.configure.requestinformation" />.
1387            </para>
1388          </listitem>
1389          </itemizedlist>
1390         
1391          </para>
1392        </listitem>
1393        </orderedlist>
1394
1395      </sect3>   
1396   
1397      <sect3 id="plugin_developer.api.callsequence.context">
1398        <title>Checking if a plug-in can be used in a given context</title>
1399       
1400        <para>
1401          If the plug-in is an <interfacename>InteractivePlugin</interfacename>
1402          it has specified in which contexts it can be used by the
1403          information returned from <methodname>InteractivePlugin.getGuiContexts()</methodname>
1404          method. The web client uses this information to decide if,
1405          for example, an <guibutton>Run plugin</guibutton>
1406          button should be displayed on a page or not. However this is not
1407          always enough to know if the plug-in can be used or not.
1408          For example, a raw data importer plug-in can't be used to
1409          import raw data if the raw bioassay already has data.
1410          So, when the user clicks the button, the web client will
1411          load all plug-ins that maybe can be used in the given context
1412          and let each one of the check if they can be used or not.
1413        </para>
1414       
1415     
1416        <orderedlist>
1417        <listitem>
1418          <para>
1419          A new instance of the plug-in class is created. The plug-in must
1420          have a public no-argument constructor.
1421          </para>
1422        </listitem>
1423       
1424        <listitem>
1425          <para>
1426          The <methodname>Plugin.init()</methodname> method is called.
1427          The <varname>job</varname> parameter is <constant>null</constant>.
1428          The <varname>configuration</varname> parameter is <constant>null</constant>
1429          if the plug-in doesn't have any configuration parameters.
1430          </para>
1431        </listitem>
1432       
1433        <listitem>
1434          <para>
1435          The <methodname>InteractivePlugin.isInContext()</methodname>
1436          is called. If the context is a list context, the <varname>item</varname>
1437          parameter is null, otherwise the current item is passed. The plug-in
1438          should return <constant>null</constant> if it can be used under the
1439          current circumstances, or a message explaining why not.
1440          </para>
1441        </listitem>
1442       
1443        <listitem>
1444          <para>
1445            After this, the plug-in instance is discarded. If there are
1446            several configurations for a plug-in this procedure is repeated
1447            for each configuration.
1448          </para>
1449
1450          <warning>
1451          <para>
1452            The <methodname>Plugin.done()</methodname> method is never
1453            called! This is bug that has been reported to the developers
1454            and hopefully it will be fixed shortly.
1455          </para>
1456          </warning>
1457        </listitem>
1458        </orderedlist>
1459      </sect3>
1460   
1461      <sect3 id="plugin_developer.api.callsequence.job">
1462        <title>Creating a new job</title>
1463       
1464        <para>
1465          If the web client found that the plug-in could be
1466          used in a given context and the user selected the plug-in
1467          the job configuration sequence is started. It is a wizard-like interface
1468          identical to the configuration wizard. In fact, the same JSP pages,
1469          and calling sequence is used. See <xref linkend="plugin_developer.api.callsequence.configure" />.
1470          We don't repeat everything here. There are a few differences:
1471        </para>
1472       
1473        <itemizedlist>
1474        <listitem>
1475          <para>
1476          The <varname>job</varname> parameter is not null, but it doesn't contain
1477          any parameter values to start with. The plug-in should use this
1478          object to store job-related parameter values. The
1479          <varname>configuration</varname> parameter is <constant>null</constant> 
1480          if the plug-in is started without configuration. In any case,
1481          the configuration values are write-protected and can't be modified.
1482          </para>
1483        </listitem>
1484       
1485        <listitem>
1486          <para>
1487          The first call to <methodname>InteractivePlugin.getRequestInformation()</methodname>
1488          is done with <constant>Request.COMMAND_CONFIGURE_JOB</constant> (_configjob)
1489          as the command. The <varname>context</varname> parameter reflects the
1490          current context.
1491          </para>
1492        </listitem>
1493       
1494        <listitem>
1495          <para>
1496          When calling <methodname>Response.setDone()</methodname> the plug-in
1497          should use the variant that takes an estimated execution time.
1498          If the plug-in has support for immediate execution or download
1499          (export plug-ins only) it can also respond with
1500          <methodname>Response.setExecuteImmediately()</methodname> or
1501          <methodname>Response.setDownloadImmediately()</methodname>.
1502          </para>
1503         
1504          <para>
1505          If the plug-in requested and was granted immediate execution or
1506          download the same plug-in instance is used to execute the plug-in.
1507          The may be done with the same or a new thread. Otherwise, a new
1508          job is added to the job queue, the parameter value are saved
1509          and the plug-in instance is discarded after calling the
1510          <methodname>Plugin.done()</methodname> method.
1511          </para>
1512        </listitem>
1513        </itemizedlist>
1514      </sect3>
1515   
1516      <sect3 id="plugin_developer.api.callsequence.execute">
1517        <title>Executing a job</title>
1518       
1519        <para>
1520          Normally, the creation of a job and the execution of it are
1521          two different events. The execution may as well be done on a
1522          different server. See <xref linkend="installation_upgrade.jobagents" />.
1523          This means that the execution takes place in a different instance
1524          of the plug-in class than what was used for creating the job.
1525          The exception is if a plug-in supports immediate execution or download.
1526          In this case the same instance is used, and it, of course,
1527          is always executed on the web server.
1528        </para>
1529       
1530        <orderedlist>
1531        <listitem>
1532          <para>
1533          A new instance of the plug-in class is created. The plug-in must
1534          have a public no-argument constructor.
1535          </para>
1536        </listitem>
1537       
1538        <listitem>
1539          <para>
1540          The <methodname>Plugin.init()</methodname> method is called.
1541          The <varname>job</varname> parameter contains the job
1542          configuration paramters. The <varname>configuration</varname> parameter
1543          is <constant>null</constant> if the plug-in doesn't have any
1544          configuration parameters.
1545          </para>
1546        </listitem>
1547       
1548        <listitem>
1549          <para>
1550          The <methodname>Plugin.run()</methodname> method is called.
1551          It is finally time for the plug-in to do the work it has bee
1552          designed for. This method shouldn't throw any exceptions.
1553          Use the <methodname>Response.setDone()</methodname>
1554          method to report success or the <methodname>Response.setError()</methodname>
1555          to reporter errors.
1556          </para>
1557        </listitem>
1558       
1559        <listitem>
1560          <para>
1561          In both cases the <methodname>Plugin.done()</methodname>
1562          method is called and the plug-in instance is discarded.
1563          </para>
1564        </listitem>
1565       
1566        </orderedlist>
1567      </sect3>
1568   
1569    </sect2>
1570 
1571    <sect2 id="plugin_developer.api.jspparameters">
1572      <title>Using custom JSP pages for parameter input</title>
1573
1574        <para>
1575          This is an advanced option for plug-ins that require a different interface for
1576          specifying plug-in parameters than the default list showing each parameter at a
1577          time. This feature is used by setting the RequestInformation.getJspPage()
1578          property when constructing the request information object. If this property has
1579          a non-null value, the web client will send the browser to the specified JSP page
1580          instead of to the generic parameter input page.
1581        </para>
1582        <para>
1583          When setting the JSP page you should only set the file name. Don't include
1584          any path information. The web
1585          client has a special location for these JSP pages, generated from the package
1586          name of your plug-in. If the plug-in is located in the package
1587          <classname>org.company</classname>
1588          the JSP page must be located in
1589          <filename class="directory">&lt;base-dir&gt;/www/plugins/org/company/</filename>.
1590          Please note that the browser still thinks that it is showing the regular page
1591          at the usual location:
1592          <filename class="directory">&lt;base-dir&gt;/www/common/plugin/index.jsp</filename>.
1593          All links in your JSP page should be relative to that directory.
1594        </para>
1595        <para>
1596          Even if you use your own JSP page we recommend that you use the built-in
1597          facility for passing the parameters back to the plug-in. For this to work you
1598          must:
1599        </para>
1600        <itemizedlist spacing="compact">
1601          <listitem>
1602            <simpara>
1603            Generate the list of <classname>PluginParameter</classname> 
1604            objects as usual.
1605            </simpara>
1606          </listitem>
1607          <listitem>
1608            <simpara>
1609              Name all your input fields in the JSP like:
1610              <parameter>
1611                parameter:<replaceable>name-of-parameter</replaceable>
1612              </parameter>
1613            </simpara>
1614            <programlisting>
1615// Plugin generate PluginParameter
1616StringParameterType stringPT = new StringParameterType(255, null, true);
1617PluginParameter one = new PluginParameter("one", "One", "First string", stringPT);
1618PluginParameter two = new PluginParameter("two", "Two", "Second string", stringPT);
1619
1620// JSP should name fields as:
1621First string: &lt;input type="text" name="parameter:one"&gt;&lt;br&gt;
1622Second string: &lt;input type="text" name="parameter:two"&gt;
1623</programlisting>
1624          </listitem>
1625          <listitem>
1626          <simpara>
1627            Send the form to
1628            <filename>index.jsp</filename>
1629            with the <varname>ID</varname> and <varname>cmd</varname> parameters
1630            as shown below.
1631          </simpara>
1632          <programlisting>
1633&lt;form action="index.jsp" method="post"&gt;
1634&lt;input type="hidden" name="ID" value="&lt;%=ID%&gt;"&gt;
1635&lt;input type="hidden" name="cmd" value="SetParameters"&gt;
1636...
1637&lt;/form&gt;
1638</programlisting>
1639
1640          </listitem>
1641          </itemizedlist>
1642            <para>
1643              In your JSP page you will probably need to access some information like the
1644              <classname>SessionControl</classname>, <classname>Job</classname>
1645              and possible even the <classname>RequestInformation</classname>
1646              object created by your plug-in.
1647            </para>
1648            <programlisting>
1649// Get session control and it's ID (required to post to index.jsp)
1650final SessionControl sc = Base.getExistingSessionControl(pageContext, true);
1651final String ID = sc.getId();
1652
1653// Get information about the current request to the plug-in
1654PluginConfigurationRequest pcRequest =
1655   (PluginConfigurationRequest)sc.getSessionSetting("plugin.configure.request");
1656PluginDefinition plugin =
1657   (PluginDefinition)sc.getSessionSetting("plugin.configure.plugin");
1658PluginConfiguration pluginConfig =
1659   (PluginConfiguration)sc.getSessionSetting("plugin.configure.config");
1660PluginDefinition job =
1661   (PluginDefinition)sc.getSessionSetting("plugin.configure.job");
1662RequestInformation ri = pcRequest.getRequestInformation();
1663</programlisting>
1664
1665    </sect2>
1666  </sect1>
1667
1668  <sect1 id="plugin_developer.import">
1669    <title>Import plug-ins</title>
1670    <para>
1671   
1672      This documentation is only available in the old format.
1673      See <ulink url="http://base.thep.lu.se/chrome/site/doc/development/plugins/import/index.html"
1674        >http://base.thep.lu.se/chrome/site/doc/development/plugins/import/index.html</ulink>
1675    </para>
1676   
1677    <sect2 id="plugin_developer.import.autodetect">
1678      <title>Autodetecting importer</title>
1679      <para></para>
1680    </sect2>
1681   
1682    <sect2 id="plugin_developer.import.abstractflatfileimporter">
1683      <title>The AbstractFlatFileImporter superclass</title>
1684      <para></para>
1685    </sect2>
1686  </sect1>
1687
1688  <sect1 id="plugin_developer.export">
1689    <title>Export plug-ins</title>
1690    <para>TODO</para>
1691   
1692    <sect2 id="plugin_developer.export.download">
1693      <title>Immediate download of exported data</title>
1694      <para>TODO</para>
1695    </sect2>
1696  </sect1>
1697 
1698  <sect1 id="plugin_developer.analyse">
1699    <title>Analysis plug-ins</title>
1700    <para>
1701      A plug-in becomes an analysis plug-in simply by returning
1702      <constant>Plugin.MainType.ANALYSIS</constant> from the
1703      <methodname>Plugin.getMainType()</methodname> method. The information returned from
1704      <methodname>InteractivePlugin.getGuiContexts()</methodname>
1705      must include: [<constant>Item.BIOASSAYSET</constant>, <constant>Type.ITEM</constant>] since
1706      this is the only place where the web client looks for analysis plug-ins.
1707    </para>
1708   
1709    <programlisting>
1710private static final Set&lt;GuiContext&gt; guiContexts =
1711   Collections.singleton(new GuiContext(Item.BIOASSAYSET, GuiContext.Type.ITEM));
1712
1713public Set&lt;GuiContext&gt; getGuiContexts()
1714{
1715   return guiContexts;
1716}
1717</programlisting>
1718
1719      <para>
1720        More documentation is available in the old format.
1721        See <ulink url="http://base.thep.lu.se/chrome/site/doc/development/plugins/analysis/index.html"
1722          >http://base.thep.lu.se/chrome/site/doc/development/plugins/analysis/index.html</ulink>
1723      </para>
1724
1725
1726  </sect1>
1727 
1728  <sect1 id="plugin_developer.other">
1729    <title>Other plug-ins</title>
1730    <para></para>
1731   
1732    <sect2 id="plugin_developer.other.authentication">
1733      <title>Authentication plug-ins</title>
1734      <para>
1735        This documentation is only available in the old format.
1736        See <ulink url="http://base.thep.lu.se/chrome/site/doc/development/plugins/authentication/index.html"
1737          >http://base.thep.lu.se/chrome/site/doc/development/plugins/authentication/index.html</ulink>
1738      </para>
1739    </sect2>
1740   
1741    <sect2 id="plugin_developer.other.secondary">
1742      <title>Secondary file storage plugins</title>
1743      <para>
1744        This documentation is only available in the old format.
1745        See <ulink url="http://base.thep.lu.se/chrome/site/doc/development/plugins/storage/index.html"
1746          >http://base.thep.lu.se/chrome/site/doc/development/plugins/storage/index.html</ulink>
1747      </para>
1748    </sect2>
1749   
1750    <sect2 id="plugin_developer.other.unpacker">
1751      <title>File unpacker plug-ins</title>
1752      <para>
1753        TODO
1754      </para>
1755    </sect2>
1756  </sect1>
1757 
1758  <sect1 id="plugin_developer.example">
1759    <title>Example plug-ins (with download)</title>
1760    <para>
1761      <para>
1762        This documentation is only available in the old format.
1763        See <ulink url="http://base.thep.lu.se/chrome/site/doc/development/index.html#plugins"
1764          >http://base.thep.lu.se/chrome/site/doc/development/index.html#plugins</ulink>
1765      </para>
1766    </para>
1767  </sect1>
1768</chapter>
Note: See TracBrowser for help on using the repository browser.