Changeset 2151
- Timestamp:
- Apr 6, 2006, 3:00:15 PM (18 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 1 deleted
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/doc/development/index.html
r2146 r2151 267 267 268 268 <dl> 269 <dt>a) <a href="plugins/index.html">How to write plug-ins</a> - Draft</dt>269 <dt>a) <a href="plugins/index.html">How to write plug-ins</a></dt> 270 270 <dd> 271 271 Document describing how to write a plug-in for BASE 2. 272 272 </dd> 273 273 274 <dl>275 274 <dt>b) <a href="plugins/import/index.html">Plug-ins for importing data</a> - TODO</dt> 276 275 <dd> -
trunk/doc/development/plugins/index.html
r2043 r2151 3 3 $Id$ 4 4 5 Copyright (C) 2006 Jari Häkkinen, Gregory Vincic 5 Copyright (C) 2006 Jari Häkkinen, Gregory Vincic, Nicklas Nordborg 6 6 7 7 This file is part of BASE - BioArray Software Environment. … … 43 43 <div class="abstract"> 44 44 45 Draft 45 <p> 46 These instructions currently cover how to create plug-ins with the 47 Java programming language for use in BASE 2. 48 </p> 46 49 47 50 <p> 48 51 <b>Contents</b><br> 49 50 These instructions currently cover how to create plug-ins with the 51 Java programming language for use in BASE 2. The instructions will 52 be expanded to other programming languages. 53 </p> 54 52 </p> 53 <ol> 54 <li><a href="#interfaces">Interfaces you need to implement</a> 55 <ul> 56 <li><a href="#plugin">The Plugin interface</a> 57 <li><a href="#interactive">The InteractivePlugin interface</a> 58 </ul> 59 <li><a href="#packaging">Packaging and installing the plugin</a> 60 <li><a href="#organize">How to organize your plugin project</a> 61 </ol> 62 63 <p> 64 <b>See also</b> 65 </p> 66 <ul> 67 <li><a href="../overview/core/plugins.html">Internals of the core API - Plugin execution</a> 68 </ul> 69 55 70 <p class=authors> 56 71 <b>Last updated:</b> $Date$ <br> … … 59 74 </div> 60 75 61 <p> 62 A sample plug-in is available for 63 download, <a href=JFreePlotPlugin.tar.gz>JFreePlotPlugin</a> 64 </p> 65 66 <h3>General steps to create a Java based plug-in</h3> 67 68 <p> 69 <ol type=i> 70 71 <li> 72 <p> 73 Create the plug-in directory layout: 74 <pre class="code"> 75 PLUGINNAME/ 76 PLUGINNAME/bin/ 77 PLUGINNAME/lib/ 78 PLUGINNAME/src/PATH/TO/PLUGINNAME 79 </pre> 80 </p> 81 </li> 82 83 <li> 84 <p> 85 Add a build file <tt>build.xml</t> in the <tt>PLUGINNAME</tt> 86 root directory: 87 <pre class="code"> 88 PLUGINNAME/ 89 PLUGINNAME/build.xml 90 </pre> 91 </p> 92 </li> 93 94 <li> 95 <p> 96 Configure <tt>build.xml</tt>, change the property value for 97 <tt>plugin.name</tt> and <tt>src</tt>: 98 <pre class="code"> 99 <?xml version="1.0" encoding="UTF-8"?> 100 <project name="${plugin.name}" default="build.plugin" basedir="."> 101 <property name="plugin.name" value="PLUGINNAME" /> 102 <property name="src" value="PATH/TO/PLUGINNAME"/> 103 104 <path id="classpath"> 105 <fileset dir="lib"> 106 <include name="**/*.jar"/> 107 </fileset> 108 </path> 109 <target name="build.plugin" description="Compiles the plugin"> 110 <javac 111 encoding="ISO-8859-1" 112 srcdir="${src}/${plugin.name}" 113 destdir="bin/" 114 classpathref="classpath"> 115 </javac> 116 <jar 117 jarfile="${plugin.name}.jar" 118 basedir="bin" 119 includes="**/*.class" 120 /> 121 </target> 122 </project> 123 </pre> 124 </p> 125 </li> 126 127 <li> 128 <p> Implement the plug-in in the 129 directory <tt>PLUGINNAME/src/path/to/PLUGINNAME/PLUGINNAME.java</tt> 130 </p> 131 </li> 132 133 <li> 134 <p> Build the plug0in with the <tt>ant</tt> tool, <i>i.e.</i> 135 do <tt>cd PLUGINNAME ; ant</tt>. If all went 136 fine <tt>PLUGINNAME/PLUGINNAME.jar</tt> will be created. 137 </p> 138 </li> 139 140 <li> 141 <p> Copy <tt>PLUGINNAME.jar</tt> and other external jar files that 142 the plug-in depends on (jar files stored 143 in <tt>PLUGINNAME/lib/</tt>) 144 to <tt>/path/to/tomcat/webapps/base2/WEB-INF/lib/</tt>. 145 </p> 146 </li> 147 148 <li> 149 <p> Create a plug-in definition through the administration 150 interface in BASE 2 web application. 151 </p> 152 </li> 153 154 <li> 155 <p> 156 That's it. 157 </p> 158 </li> 159 160 </ol> 161 </p> 162 163 <p> 164 The problem now is to create the plug-in. Information about the 165 plug-in interfaces are currently available in 166 <a href=http://base.thep.lu.se/base2/api/develop/index.html>core 167 javadoc</a> format. 168 </p> 169 76 <a name="interfaces"></a> 77 <h2>1. Interfaces you need to implement</h2> 78 <p> 79 The Base2 core defined two interfaces that are vital for implementing plugins: 80 </p> 81 82 <ul> 83 <li><code>net.sf.basedb.core.plugin.Plugin</code> 84 <li><code>net.sf.basedb.core.plugin.InteractivePlugin</code> 85 </ul> 86 87 It is required that the <code>Plugin</code> interface is implemented, but the 88 <code>InteractivePlugin</code> is optional, and is only needed if you want user 89 interaction. 90 91 <a name="plugin"></a> 92 <h3>1.1 The <code>Plugin</code> interface</h3> 93 94 <p> 95 This interface defines five methods and must be implemented by all plugins: 96 </p> 97 98 <dl> 99 <dt class="method">public About getAbout();</dt> 100 <dd> 101 <p> 102 Return information about the plugin. Ie. The name, version and a short description 103 about what the plugin does. The <code>About</code> object also has fields for 104 naming the author and various other contact information. The returned information 105 is copied by the core at installation time to the database. The only required information 106 is the name of the plugin. All other fields may have null values. 107 </p> 108 <p> 109 A typical implementation stores this information in a static field: 110 </p> 111 112 <pre class="code"> 113 private static final About about = 114 new AboutImpl 115 ( 116 "Spot images creator", 117 "Converts a full-size scanned image into smaller preview jpg " + 118 "images for each individual spot.", 119 "2.0", 120 "2006, Department of Theoretical Physics, Lund University", 121 null, 122 "base@thep.lu.se", 123 "http://base.thep.lu.se" 124 ); 125 126 public About getAbout() 127 { 128 return about; 129 } 130 </pre> 131 </dd> 132 133 <dt class="method">public Plugin.MainType getMainType();</dt> 134 <dd> 135 <p> 136 Return information about the main type of plugin. The <code>MainType</code> 137 is an enumeration which defines five possible values: 138 </p> 139 140 <ul> 141 <li><code>ANALYZE</code>: An analysis plugin 142 <li><code>EXPORT</code>: A plugin the exports data 143 <li><code>IMPORT</code>: A plugin that imports data 144 <li><code>INTENSITY</code>: A plugin that calculates the original spot intensities 145 from raw data 146 <li><code>OTHER</code>: Any other type of plugin 147 </ul> 148 149 <p> 150 The returned value is stored in the database but is otherwise 151 not used by the core. Client applications (such as the web client) 152 will probably use this information to group the plugins. Ie. a button 153 labeled <code>Export</code> will let you select among the export plugins. 154 </p> 155 156 <p> 157 A typical implementation just return one of the values: 158 </p> 159 160 <pre class="code"> 161 public Plugin.MainType getMainType() 162 { 163 return Plugin.MainType.OTHER; 164 } 165 </pre> 166 167 </dd> 168 169 <dt class="method">public void init(SessionControl sc, 170 ParameterValues configuration, ParameterValues job) 171 throws BaseException;</dt> 172 173 <dd> 174 <p> 175 Prepare the plugin for execution (or configuration). If the plugin needs to 176 do some initialization this is the place to do it. A typical implementation 177 however only stores the passed parameters in instance variables for later use. 178 </p> 179 180 <p> 181 The parameters passed to this method has vital information that is needed 182 to execute the plugin. The <code>SessionControl</code> is a central core 183 object which holds information about the logged in used and allows you 184 to create <code>DbControl</code> objects which allows a plugin to connect 185 to the database to read, add or update information. The two 186 <code>ParameterValues</code> objects contains information about the parameters 187 to the plugin. The <code>configuration</code> object holds all parameters 188 stored together with a <code>PluginConfiguration</code> object in the database. 189 The <code>job</code> object holds all parameters that are stored together with a 190 <code>Job</code> object in the database. 191 </p> 192 193 <p> 194 The difference between a plugin configuration and a job parameter is that 195 a configuration is usually something an administrator sets up, while 196 a job is an actual execution of a plugin. For example a configuration 197 for an import plugin holds the regular expressions needed to parse a text 198 file and find the headers, sections and data lines, while the job holds 199 the file to parse. 200 </p> 201 202 <p> 203 The <code>AbstractPlugin</code> contains an implementation of this method 204 make the passed parameters available as protected 205 instance variables. We recommend plugin developers to let their plugins 206 extend this class since it also has some other useful methods. For example 207 for validating parameters resulting from user interaction and to store these 208 values in the database. 209 </p> 210 211 <p> 212 The <code>AbstractPlugin</code> implementation of this method. 213 <pre class="code"> 214 protected SessionControl sc = null; 215 protected ParameterValues configuration = null; 216 protected ParameterValues job = null; 217 /** 218 Store copies of the session control, plugin and job configuration. These 219 are available to subclasses in the {@link #sc}, {@link #configuration} 220 and {@link #job} variables. If a subclass overrides this method it is 221 recommended that it also calls super.init(sc, configuration, job). 222 */ 223 public void init(SessionControl sc, 224 ParameterValues configuration, ParameterValues job) 225 throws BaseException 226 { 227 this.sc = sc; 228 this.configuration = configuration; 229 this.job = job; 230 } 231 </pre> 232 </dd> 233 234 <dt class="method">public void run(Request request, Response response, ProgressReporter progress);</dt> 235 <dd> 236 <p> 237 Runs the plugin. The <code>Request</code> parameter has no useful information 238 and can be ignored. It was originally used for passing parameters to the plugin 239 but this is now found in the two <code>ParameterValues</code> objects passed 240 to the <code>init</code> method. 241 </p> 242 243 <p> 244 The <code>ProgressReporter</code> can be used by a plugin to report it's progress 245 back to the core. The core will usually send the progress information to the database, 246 which allows users to see exactly how the plugin is progressing from the web 247 interface. This parameter can be null, but if it isn't we recommend all plugins 248 to use it. However, it should be used sparingly, since each call to set the progress 249 results in a database update. If the execution involves several thousands of items 250 it is a bad idea to update the progress after processing each one of them. A good starting 251 point is to divide the work into 100 pieces each representing 1% of the work. Ie. 252 if the plugin should export 100 000 items it should report progress after every 1000 253 items. 254 </p> 255 256 <p> 257 The <code>Response</code> parameter is used to tell the core if the plugin 258 was successful or failed. Not setting a response is considered a failure by the 259 core. From the <code>run</code> method it is only allowed to use the 260 <code>setDone()</code> or the <code>setError()</code> methods. 261 </p> 262 263 <p> 264 Here is a skeleton that we recommend each plugin to use in it's implementation 265 of the <code>run</code> method: 266 </p> 267 268 <pre class="code"> 269 public void run(Request request, Response response, ProgressReporter progress) 270 { 271 // Open a connection to the database 272 // sc is set by init() method 273 DbControl dc = sc.newDbControl(); 274 try 275 { 276 // Insert code for plugin here 277 278 // Commit the work 279 dc.commit(); 280 response.setDone("Plugin ended successfully"); 281 } 282 catch (Throwable t) 283 { 284 // All exceptions must be catched and sent back 285 // using the response object 286 response.setError(t.getMessage(), Arrays.asList(t)); 287 } 288 finally 289 { 290 // IMPORTANT!!! Make sure opened connections are closed 291 if (dc != null) dc.close(); 292 } 293 } 294 </pre> 295 </dd> 296 297 <dt class="method">public void done();</dt> 298 <dd> 299 <p> 300 Clean up all resources after executing the plugin. This method 301 mustn't throw any exceptions. 302 </p> 303 <p> 304 The <code>AbstractPlugin</code> contains an implementation of 305 this method which simply sets the parameters passed to the <code>init</code> 306 method to null: 307 </p> 308 309 <pre class="code"> 310 /** 311 Clears the variables set by the <code>init</code> method. If a subclass 312 overrides this method it is recommended that it also calls <code>super.done()</code>. 313 */ 314 public void done() 315 { 316 configuration = null; 317 job = null; 318 sc = null; 319 } 320 </pre> 321 322 323 </dl> 324 325 <a name="interactive"></a> 326 <h3>1.2 The <code>InteractivePlugin</code> interface</h3> 327 328 <p> 329 If you want the plugin to be able to interact with the user you must 330 also implement this interface. This is probably the case for most plugins. 331 Among the plugins supplied with the core of Base the <code>SpotImageCreator</code> 332 is one plugin that doesn't interact with the user. Instead, the web client has 333 special JSP pages that handles all the interaction, creates a job for it and 334 sets the parameters. This, kind of hardcoded, approach can be used for other plugins 335 as well, but then it usually requires modification of the client application as well. 336 </p> 337 338 <p> 339 The <code>InteractivePlugin</code> has three main tasks: tell a client application 340 where the plugin should be plugged in, ask users for parameters, and validate and store 341 those parameters. It has four methods: 342 </p> 343 344 <dl> 345 <dt class="method">public Set<GuiContext> getGuiContexts();</dt> 346 <dd> 347 <p> 348 Return information about where the plugin should be plugged in. Each 349 place is identified by a <code>GuiContext</code> object, which is 350 an <code>Item</code> and a <code>Type</code>. The item is one of the 351 objects defined by the <code>net.sf.basedb.core.Item</code> enumeration 352 and the type is either <code>Type.LIST</code> or <code>Type.ITEM</code>. 353 </p> 354 355 <p> 356 For example, the <code>GuiContext = (Item.REPORTER, Type.LIST)</code> 357 tells a client application that this plugin can be plugged in whenever 358 a list of reporters is displayed. The 359 <code>GuiContext = (Item.REPORTER, Type.ITEM)</code> tells a client application 360 that this plugin can be plugged in whenever a list of reporters is displayed. 361 The first case may be appropriate for a plugin that imports or exports reporters. 362 The second case may be used by a plugin that updates the reporter information 363 from an external source (well, it may make sense to use this in the list case 364 as well). 365 </p> 366 367 <p> 368 The returned information is copied by the core at installation time 369 to make it easy to ask for all plugins for a certain <code>GuiContext</code>. 370 </p> 371 372 <p> 373 A typical implementation creates a static <b>unmodifable</b> <code>Set</code> 374 which is returned by this method. It is important that the returned set can't 375 be modified, since it may be a security issue if a bad behaving client 376 application does that. 377 </p> 378 379 <pre class="code"> 380 // From the net.sf.basedb.plugins.RawDataFlatFileImporter plugin 381 private static final Set<GuiContext> guiContexts = 382 Collections.singleton(new GuiContext(Item.RAWBIOASSAY, GuiContext.Type.ITEM)); 383 384 public Set<GuiContext> getGuiContexts() 385 { 386 return guiContexts; 387 } 388 </pre> 389 </dd> 390 391 <dt class="method">public boolean isInContext(GuiContext context, Object item);</dt> 392 <dd> 393 <p> 394 This method is called to check if a particular item is usable for the plugin, 395 when the context type is <code>Type.ITEM</code>. Ie. the user has selected 396 a specific <code>SAMPLE</code> and the the client application is now displaying 397 information about that sample. Thus, our <code>GuiContext = (Item.SAMPLE, Type.ITEM)</code>. 398 Now, the client application asks for a list of plugins supporting this context 399 and for each one in the list calls this method with the current sample as 400 the <code>item</code> parameter. The plugin should answer if it can do whatever 401 it is supposed to do by returning <code>true</code> or <code>false</code>. 402 </p> 403 404 <p> 405 Here is a real example from the <code>RawDataFlatFileImporter</code> plugin 406 which imports raw data to a <code>RawBioAssay</code>. Thus, 407 <code>GuiContext = (Item.RAWBIOASSAY, Type.ITEM)</code>, but the plugin can 408 only import data if there isn't any already, and if the raw bioassay has the 409 same raw data type as the plugin has been configured for. 410 </p> 411 412 <pre class="code"> 413 /** 414 Returns TRUE if the item is a {@link RawBioAssay} of the correct 415 {@link RawDataType} and doesn't already have spots. 416 */ 417 public boolean isInContext(GuiContext context, Object item) 418 { 419 boolean inContext = false; 420 if (item instanceof RawBioAssay) 421 { 422 RawBioAssay rba = (RawBioAssay)item; 423 if (rba.getSpots() == 0) 424 { 425 String actualRawDataType = rba.getRawDataType().getId(); 426 String configuredRawDataType = (String)configuration.getValue("rawDataType"); 427 inContext = actualRawDataType.equals(configuredRawDataType); 428 } 429 } 430 return inContext; 431 } 432 </pre> 433 434 </dd> 435 436 <dt class="method">public RequestInformation getRequestInformation(GuiContext context, String command) 437 throws BaseException;</dt> 438 <dd> 439 <p> 440 Ask the plugin for parameters that needs to be entered by the user. The 441 <code>GuiContext</code> parameter is one of the contexts returned by the 442 <code>getGuiContexts</code> method. The command is string telling the plugin 443 what command was executed. There are two predefined commands but as you will see 444 the plugin may define it's own commands. The two predefined commands are defined 445 in the <code>net.sf.basedb.core.plugin.Request</code> class: 446 </p> 447 448 <ul> 449 <li><code>Request.COMMAND_CONFIGURE_PLUGIN</code>: Used when an administator is 450 initiating a configuration of the plugin. 451 <li><code>Request.COMMAND_CONFIGURE_JOB</code>: Used when a user has selected 452 the plugin for running a job. 453 </ul> 454 455 <p> 456 Given this information the plugin must return a <code>RequestInformation</code> 457 object. This is simply a title, a description and a list of parameters. 458 Usually the title will end up as the input form title and the description 459 as a help text for the entire form. Do not put information about the 460 individual parameters in this description, since each parameter has a 461 description of their own. 462 </p> 463 464 <p> 465 For example, when runing an import plugin it needs to ask for the file to 466 import from and if existing items should be updated or not: 467 </p> 468 469 <pre class="code"> 470 // The complete request information 471 private RequestInformation configureJob; 472 473 // The parameter that asks for a file to import from 474 private PluginParameter<File> fileParameter; 475 476 // The parameter that asks if existing items should be updated or not 477 private PluginParameter<Boolean> updateExistingParameter; 478 479 public RequestInformation getRequestInformation(GuiContext context, String command) 480 throws BaseException 481 { 482 RequestInformation requestInformation = null; 483 if (command.equals(Request.COMMAND_CONFIGURE_PLUGIN)) 484 { 485 requestInformation = getConfigurePlugin(); 486 } 487 else if (command.equals(Request.COMMAND_CONFIGURE_JOB)) 488 { 489 requestInformation = getConfigureJob(); 490 } 491 return requestInformation; 492 } 493 494 /** 495 Get (and build) the request information for starting a job. 496 */ 497 private RequestInformation getConfigureJob() 498 { 499 if (configureJob == null) 500 { 501 fileParameter = new PluginParameter<File>( 502 "file", 503 "File", 504 "The file to import the data from", 505 new FileParameterType(null, true, 1) 506 ); 507 508 updateExistingParameter = new PluginParameter<Boolean>( 509 "updateExisting", 510 "Update existing items", 511 "If this option is selected, already existing items will be updated " + 512 " with the information in the file. If this option isn't selected " + 513 " existing items are left untouched.", 514 new BooleanParameterType(false, true) 515 ); 516 517 List<PluginParameter<?>> parameters = 518 new ArrayList<PluginParameter<?>>(2); 519 parameters.add(fileParameter); 520 parameters.add(updateExistingParameter); 521 522 configureJob = new RequestInformation 523 ( 524 Request.COMMAND_CONFIGURE_JOB, 525 "Select a file to import items from", 526 "TODO - description", 527 parameters 528 ); 529 } 530 return configureJob; 531 } 532 </pre> 533 534 <p> 535 As you can see it takes some code to put together a <code>RequestInformation</code> 536 object. For each parameter needed you need one <code>PluginParameter</code> 537 object and one <code>ParameterType</code> object. Actually, a <code>ParameterType</code> 538 can be reused for more than one <code>PluginParameter</code>. For example, if 539 your plugin need 10 string which all are required you can use a single 540 <code>ParameterType</code> for all of them: 541 </p> 542 543 <pre class="code"> 544 StringParameterType stringPT = new StringParameterType(255, null, true); 545 PluginParameter one = new PluginParameter("one", "One", "First string", stringPT); 546 PluginParameter two = new PluginParameter("two", "Two", "Second string", stringPT); 547 // ... and so on 548 </pre> 549 550 <p> 551 The <code>ParameterType</code> is an abstract base class for several subclasses 552 each implementing a specific type of parameter. The list of subclasses may 553 grow in the future, but here are the most important ones currently implemented: 554 </p> 555 556 <p> 557 Note! Most parameter types include support for suppying a predefined list 558 of options to select from. In that case the list will be displayed as drop-down 559 list for the user, otherwise a free input field is used. 560 </p> 561 562 <ul> 563 <li><code>StringParameterType</code>: Asks for a string value. Includes 564 an option for specifying the maximum length of the string. 565 <li><code>FloatParameterType, DoubleParameterType, IntegerParameterType, LongParameterType</code>: 566 Asks for numerical values. Includes options for specifying a range (min/max) 567 of allowed values. 568 <li><code>BooleanParameterType</code>: Asks for a boolean value. 569 <li><code>DateParameterType</code>: Asks for a date. 570 <li><code>FileParameterType</code>: Asks for a file item. 571 <li><code>ItemParameterType</code>: Asks for any other item. This parameter 572 type requires that a list of options is supplied, except when the item type 573 asked for matches the current <code>GuiContext</code>, in which case the 574 currently selected item is used as the parameter value. 575 <li><code>PathParameterType</code>: Ask for a path to a file or directory. 576 The path may be non-existing and should be used when a plugin needs an 577 output destination. Ie. the file to export to, or a directory 578 where the output files should be placed. 579 </ul> 580 581 </dd> 582 583 <dt class="method">public void configure(GuiContext context, Request request, Response response);</dt> 584 <dd> 585 <p> 586 Sends parameter values entered by the user for processing by the plugin. 587 Typically the plugin should validate that the parameter values are correct 588 and then store them in database. 589 </p> 590 591 <p> 592 No validation is done by the core, except converting the input to the 593 correct object type, ie. if the parameter asked for a Float the input string 594 is parsed and converted to a Float. If you have extended the 595 <code>AbstractPlugin</code> class it is very easy to validate the parameters 596 using it's <code>validateRequestParameters()</code> method. This method 597 takes the same list of <code>PluginParameter</code>:s used in the 598 <code>RequestInformation</code> object and uses that information for validation. 599 It returns null or a list of <code>Throwable</code>. 600 </p> 601 602 <p> 603 When the parameters have been validated thay need to be stored. Once again, it is 604 very easy if you use one of the <code>AbstractPlugin.storeValue()</code> or 605 <code>AbstractPlugin.storeValues()</code> methods. 606 </p> 607 608 <p> 609 The <code>configure</code> method works much like the <code>Plugin.run</code> 610 method. It must return the result in the <code>Response</code> object. 611 Ie. it shouldn't trow any exceptions. Here is an example of part of an 612 implementation (building on the example above). 613 </p> 614 615 <pre class="code"> 616 public void configure(GuiContext context, Request request, Response response) 617 { 618 String command = request.getCommand(); 619 try 620 { 621 if (command.equals(Request.COMMAND_CONFIGURE_PLUGIN)) 622 { 623 // TODO 624 } 625 else if (command.equals(Request.COMMAND_CONFIGURE_JOB)) 626 { 627 // Validate user input 628 List<Throwable> errors = 629 validateRequestParameters(getConfigureJob().getParameters(), request); 630 if (errors != null) 631 { 632 response.setError(errors.size() + 633 " invalid parameter(s) were found in the request", errors); 634 return; 635 } 636 637 // Store user input 638 storeValue(job, request, fileParameter); 639 storeValue(job, request, updateExistingParameter); 640 641 // We are happy and done 642 response.setDone("Job configuration complete", Job.ExecutionTime.SHORT); 643 // TODO - check file size to make a better estimate of execution time 644 } 645 } 646 catch (Throwable ex) 647 { 648 response.setError(ex.getMessage(), Arrays.asList(ex)); 649 } 650 } 651 </pre> 652 653 <p> 654 Note that the <code>setDone()</code> has a second parameter <code>Job.ExecutionTime</code>. 655 It is an indication about how long time it will take to execute the plugin. This is 656 of interest for job queue managers which probably doesn't want to start too many 657 long-running jobs at the same time blocking the entire system. Please 658 try to use this parameter wisely and not use the <code>SHORT</code> value out of 659 old habit all the time. 660 </p> 661 662 <p> 663 The response also has a <code>setContinue()</code> method which tells the core 664 that the plugin needs more parameters. Ie. the core will then call 665 <code>getRequestInformation()</code> again with the new command, let the user 666 enter values, and the call <code>configure()</code> with the new values. 667 This process is repeated until the plugin reports that it is done or 668 an error occurs. 669 </p> 670 671 <p> 672 An important note is that during this iteration it is the same instance 673 of the plugin that is used. However, no parameter values are stored in the database 674 until <code>setDone()</code> is called. Then, the plugin instance is usually 675 discarded. The execution of the plugin happens in a new instance and maybe 676 on a different server. 677 </p> 678 679 <p> 680 Tip! You doesn't have to store all values the plugin asked for in the first 681 place. You may even choose to store different values than those that were 682 entered. For example, you might ask for the mass and height of a person and 683 then only store the body mass index, which is calculated from those values. 684 </p> 685 686 687 </dd> 688 689 </dl> 690 691 <a name="packaging"></a> 692 <h2>2. Packaging and installing the plugin </h2> 693 694 <p> 695 We recommend that each plugin or group of related plugins are compiled 696 separately. To be able to use the plugin it must be put in a JAR file. 697 Place the JAR file on the server <b>outside</b> the web servers classpath, ie. not in 698 the <code>WEB-INF/lib</code>. Our recommendation is to place the plugin JAR in 699 <code><base-dir>/plugins/<name-of-plugin>/</code> 700 </p> 701 <img src="install_plugin.png" alt="How to install a plugin" align="right"> 702 703 <p> 704 The main benefit from placing the JAR file outside the classpath is that 705 Base uses it's own classloader that supports unloading of the classes as well. 706 This means that you may replace the JAR file with a new version without 707 restarting the web server. 708 </p> 709 710 <p> 711 Then, to install the plugin log in a an administrator and go to the 712 <code>Administrate --> Plugins --> Definitions</code> 713 page. Click the <code>New…</code> button and enter the 714 class name and the path to the JAR file in the form that opens 715 in the popup window. 716 </p> 717 718 <p> 719 When you click save, the Base class loader will load the specified JAR file 720 and class and check that it implements the <code>Plugin</code> interface. 721 Then, it creates an instance of that class, calls <code>Plugin.getAbout()</code> 722 and <code>Plugin.getMainType()</code>. If it is an <code>InteractivePlugin</code> 723 it will also call <code>InteractivePlugin.getGuiContexts()</code>. This information 724 is stored in the database. 725 </p> 726 727 <p> 728 The installation will do one more thing. It will check which other interfaces the 729 plugin implements and check against the list of registered <code>PluginType</code>:s. 730 The <code>PluginType</code> system has not really been put into use yet. The core 731 defines the <code>AutoDetectingImporter</code> which can be used for all import plugins 732 that supports automatic detection of file formats. Read more about this in the 733 <a href="import/index.html">Plug-ins for importing data</a> document. 734 </p> 735 736 <p> 737 Now the administrator may continue by creating a new configuration for the 738 plugin (assuming that is an <code>InteractivePlugin</code>. When the 739 administrator starts the configuration sequence the 740 following will happen: 741 </p> 742 743 <ul> 744 <li>The core creates a new instance of the plugin. 745 <li>Call the <code>Plugin.init()</code> method. 746 <li>Call the <code>InteractivePlugin.getRequestInformation()</code> method, 747 with <code>command = Request.COMMAND_CONFIGURE_PLUGIN</code> and a null 748 <code>GuiContext</code>. 749 <li>Display the list of parameters and let the user enter values. 750 <li>Call <code>InteractivePlugin.configure()</code>. 751 <li>If the plugin wants more parameters the above two steps are repeated 752 but with the command returned in the response. Note! Be careful 753 so you don't create infinite loops. 754 <li>If the plugin reports that it is done, <code>Plugin.done()</code> 755 is called and the plugin instance is discarded. 756 </ul> 757 758 <p> 759 The steps for creating a new job follows the same procedure except that 760 the first command is <code>Request.COMMAND_CONFIGURE_JOB</code> and 761 the <code>GuiContext</code> isn't null. 762 </p> 763 764 765 <a name="organize"></a> 766 <h2>3. How to organize your plugin project</h2> 767 <p> 768 Here is a simple example of how you might organize your project using 769 ant (<a href="http://ant.apache.org">http://ant.apache.org</a>) as the build tool. 770 This is just a recommendation that we have found to be working well. You may choose 771 to do it another way. 772 </p> 773 774 <h3>3.1 Directory layout</h3> 775 776 <pre class="code"> 777 PLUGINNAME/ 778 PLUGINNAME/bin/ 779 PLUGINNAME/lib/ 780 PLUGINNAME/src/org/company/ 781 </pre> 782 783 <p> 784 The <code>bin/</code> directory is empty to start with. It will contain the 785 compiled code. The <code>lib/</code> directory contains the JAR files 786 your plugin uses (including the BASE2Core.jar). The <code>src/</code> 787 directory contains your source code. 788 </p> 789 790 <h3>3.2 Ant build file</h3> 791 792 <p> 793 In the root of your directory, create the build file: <code>build.xml</code>. 794 Here is an example that will compile your plugin and put it in a JAR file. 795 </p> 796 797 <iframe src="build.txt" width="100%" height="550" class="code" ></iframe> 798 799 <p> 800 If your plugin depends on other JAR files than the <code>Base2Core.jar</code> 801 you should list them in the MANIFEST.MF file. Otherwise you should remove the 802 <code>manifest</code> attribute of the <code>jar</code> tag in the build file. 803 </p> 804 805 <pre class="code"> 806 Manifest-Version: 1.0 807 Class-Path: OtherJar.jar ASecondJar.jar 808 </pre> 809 810 <h3>3.3 Building the plugin</h3> 811 <p> 812 Compile the plugin simply by typing <code>ant</code> in the console 813 window. If all went well the <code>MyPlugin.jar</code> will be 814 created in the same directory. 815 <p> 816 817 <p> 818 To install the plugin copy the JAR file to the server including the 819 dependent JAR files (if any). Place all files together in the same 820 directory. Then follow the instructions in section 2 821 for making Base aware of the plugin. 822 </p> 823 170 824 </body> 171 825 </html> -
trunk/src/core/net/sf/basedb/plugins/RawDataFlatFileImporter.java
r1795 r2151 250 250 { 251 251 RawBioAssay rba = (RawBioAssay)item; 252 String rawDataType = (String)configuration.getValue(rawDataTypeParameter.getName()); 253 inContext = rba.getRawDataType().getId().equals(rawDataType) && rba.getSpots() == 0; 252 if (rba.getSpots() == 0) 253 { 254 String rawDataType = (String)configuration.getValue(rawDataTypeParameter.getName()); 255 inContext = rba.getRawDataType().getId().equals(rawDataType); 256 } 254 257 } 255 258 return inContext;
Note: See TracChangeset
for help on using the changeset viewer.