Changeset 4202


Ignore:
Timestamp:
Apr 1, 2008, 2:55:44 PM (15 years ago)
Author:
Nicklas Nordborg
Message:

References #436: Create extension system for the web application

All major features are now in place. Next step is to create more documentation and weed out remaining bugs and other irregularities. It should now be possible to start with beta-testing of actually developing external extensions.

Location:
trunk
Files:
10 added
16 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/ExtensionsControl.java

    r4198 r4202  
    2828import java.util.Set;
    2929import java.util.TimerTask;
     30
     31import javax.servlet.jsp.PageContext;
    3032
    3133import net.sf.basedb.clients.web.servlet.ExtensionsServlet;
     
    112114    // Load settings
    113115    File settingsFile = new File(directory.getExtensionDirectory(), "settings.xml");
    114     settings = new Settings(settingsFile);
     116    settings = new Settings(extensionsDir, settingsFile);
    115117    extensionsDir.addIgnore(settingsFile);
    116118   
     
    187189  }
    188190 
     191  /**
     192    Create a new {@link JspContext} object.
     193    @param sc The current session control
     194    @param pageContext Page context for the executing JSP page
     195  */
     196  public static JspContext createContext(SessionControl sc, PageContext pageContext)
     197  {
     198    return new JspContext(extensionsDir, sc, pageContext);
     199  }
    189200 
    190201  private final Set<Permission> permissions;
     
    435446  public ExtensionsFile getFileByExtensionId(String extensionId)
    436447  {
    437     Object o = registry.getAttribute(extensionId, "FILE");
    438     ExtensionsFile file = null;
    439     if (o instanceof ExtensionsFile)
    440     {
    441       file = (ExtensionsFile)o;
    442     }
    443     return file;
     448    return extensionsDir.getFileByExtensionId(extensionId);
    444449  }
    445450 
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/ExtensionsDirectory.java

    r4198 r4202  
    3434import java.util.Map;
    3535import java.util.Set;
     36import java.util.TreeMap;
    3637import java.util.regex.Pattern;
    3738
     
    8081    this.rootUrl = rootUrl;
    8182   
    82     this.installedFiles = new HashMap<File, ExtensionsFile>();
     83    this.installedFiles = new TreeMap<File, ExtensionsFile>();
    8384    this.installedExtensions = new HashMap<String, ExtensionsFile>();
    8485    this.staticIgnore = new HashSet<File>();
     
    112113  /**
    113114    Get the URL that points to the resources directory when accessed
    114     through the web server. The URL is not a full URL, it doesn't include
    115     the server name. For example, /base2/resources
     115    through the web server. The URL only includes the local server path,
     116    not the server name. For example, /base2/resources
    116117    @return The URL
    117118  */
     
    122123 
    123124  /**
     125    Get the URL that points to the resources directory for a specific
     126    extensions file when accessed through the web server. The URL only
     127    includes the local server path, not the server name. For example,
     128    /base2/resources/example-extensions.jar
     129    @return The URL
     130  */
     131  public String getResourcesUrl(ExtensionsFile extFile)
     132  {
     133    return resourcesUrl + "/" + extFile.getName();
     134  }
     135 
     136  /**
    124137    Get the URL that points to the root directory when accessed
    125     through the web server. The URL is not a full URL, it doesn't include
    126     the server name. For example, /base2
     138    through the web server. The URL only includes the local server path,
     139    not the server name. For example, /base2
    127140    @return The URL
    128141  */
     
    178191  {
    179192    return installedFiles.get(new File(extensionsDir, filename));
     193  }
     194 
     195  /**
     196    Get information about the file a given extension or extension
     197    point is defined in.
     198    @param extensionId The ID of an extension or extension point
     199    @return Information about the file the extension is defined in,
     200      or null if no extension with the given ID is found
     201  */
     202  ExtensionsFile getFileByExtensionId(String extensionId)
     203  {
     204    return installedExtensions.get(extensionId);
     205  }
     206
     207  void addRegistered(String id, ExtensionsFile file)
     208  {
     209    ExtensionsFile other = installedExtensions.get(id);
     210    if (other == null)
     211    {
     212      installedExtensions.put(id, file);
     213    }
     214    else if (other != file)
     215    {
     216      throw new RuntimeException("Extension '" + id +
     217        "' is already registered in file: " + other.getName());
     218    }
     219  }
     220 
     221  void removeRegistered(String id, ExtensionsFile file)
     222  {
     223    ExtensionsFile other = installedExtensions.get(id);
     224    if (other == file)
     225    {
     226      installedExtensions.remove(id);
     227    }
    180228  }
    181229 
     
    202250    try
    203251    {
    204       // Re-scan failed JAR/XML files; Doesn't affect manually added ignore files
    205       if (forceUpdate) resetIgnore();
     252      if (forceUpdate)
     253      {
     254        // Re-scan failed JAR/XML files; Doesn't affect manually added ignore files
     255        resetIgnore();
     256        installedExtensions.clear();
     257      }
    206258     
    207259      // 1. Unregister all extensions that no longer exists
     
    271323
    272324          // Remove extracted resources
    273           File homeDir = new File(resourcesDir, extFile.getName());
    274           int numResources = extFile.removeResources(homeDir);
     325          int numResources = extFile.removeResources();
    275326         
    276327          // Set scan result messages
     
    284335        catch (Exception ex)
    285336        {
     337          extFile.setError(true);
    286338          results.addErrorMessage(extFile,
    287339              "Could not unregister extensions: " + ex.getMessage());
     
    330382        log.debug("Found new file: " + file.getName());
    331383        ExtensionsFile extFile =
    332           new ExtensionsFile(this, file, createXmlLoader());
     384          new ExtensionsFile(this, file, new File(resourcesDir, file.getName()), createXmlLoader());
    333385       
    334386        // Is is a valid extensions file?
     
    346398        {
    347399          // Not a valid extensions file; add to ignore list
    348           log.info("Not a valid extensions file: " + file.getName());
     400          Throwable validationError = extFile.getValidationError();
     401          log.info("Not a valid extensions file: " + file.getName(), validationError);
    349402          ignore.add(file);
    350403         
    351404          // Set scan result messages
    352405          results.setStatus(extFile, "Ignored");
    353           results.addMessage(extFile, "Not a valid extensions XML or JAR file");
     406          String msg = validationError != null ? validationError.getMessage() : "";
     407          results.addMessage(extFile, "Not a valid extensions XML or JAR file: " + msg);
    354408        }
    355409      }
     
    411465              (isNew ? "new" : isModified ? "modified" : "forced update"));
    412466         
     467          // Reset error flag
     468          extFile.setError(false);
     469             
    413470          // Set home directory for resources on value converters
    414           String homePath = resourcesUrl + "/" + extFile.getName();
     471          String homePath = getResourcesUrl(extFile);
     472          log.debug("Home URL=" + homePath);
    415473          variableConverter.setVariable("HOME", homePath);
    416474          pathConverter.setHome(homePath);
    417           log.debug("Home URL=" + homePath);
    418475          extFile.loadExtensions();
    419476          log.debug(extFile.getName() + " has been loaded");
     
    427484      catch (Exception ex)
    428485      {
     486        extFile.setError(true);
    429487        results.addErrorMessage(extFile,
    430488            "Could not load extensions: " + ex.getMessage());
     
    455513    for (ExtensionsFile extFile : installedFiles.values())
    456514    {
     515      // Do not extract from files with an error
     516      if (extFile.hasError()) continue;
     517     
    457518      try
    458519      {
    459520        if (forceUpdate || extFile.isModified())
    460521        {
    461           File homeDir = new File(resourcesDir, extFile.getName());
    462           int numResources = extFile.extractResources(homeDir, resourcePattern, "$1", forceUpdate);
     522          int numResources = extFile.extractResources(resourcePattern, "$1", forceUpdate);
    463523          if (numResources > 0)
    464524          {
     
    470530      catch (Exception ex)
    471531      {
     532        extFile.setError(true);
    472533        results.addErrorMessage(extFile, "Could not extract resources: " + ex.getMessage());
    473534        log.error("Could not extract resources from " + extFile.getName(), ex);
     
    478539  /**
    479540    Register extensions with the registry. Unless forceUpdate is TRUE only
    480     new and modified extensions are registered.
    481   */
    482   private void registerExtensions(Registry registry, boolean forceUpdate, ScanResults results)
     541    new and modified extensions are registered. Extensions files that has
     542    the {@link ExtensionsFile#hasError()} flag set are automatically disabled.
     543  */
     544  private void registerExtensions(Registry registry,  boolean forceUpdate, ScanResults results)
    483545  {
    484546    for (ExtensionsFile extFile : installedFiles.values())
    485547    {
     548      // Update the last modified information
     549      extFile.resetModified();
     550
     551      // Do not register extensions that has an error
     552      if (extFile.hasError()) continue;
     553     
    486554      try
    487555      {
     556        extFile.unregisterMissing(registry);
    488557        int num = extFile.registerExtensionPoints(registry, forceUpdate);
    489558        if (num > 0)
     
    494563      catch (Exception ex)
    495564      {
     565        extFile.setError(true);
    496566        results.addErrorMessage(extFile,
    497567          "Could not register extension points: " + ex.getMessage());
     
    502572    for (ExtensionsFile extFile : installedFiles.values())
    503573    {
     574      // Do not register extensions that has an error
     575      if (extFile.hasError()) continue;
     576
    504577      try
    505578      {
    506579        int num = extFile.registerExtensions(registry, forceUpdate);
    507         extFile.resetModified();
    508580        results.addMessage(extFile, num + " extensions registered.");
    509581      }
    510582      catch (Exception ex)
    511583      {
     584        extFile.setError(true);
    512585        results.addErrorMessage(extFile,
    513586          "Could not register extensions: " + ex.getMessage());
     
    518591 
    519592
     593  /**
     594    Creates a new XmlLoader and registers the {@link #variableConverter}
     595    and {@link #pathConverter} with it.
     596  */
    520597  private XmlLoader createXmlLoader()
    521598  {
     
    526603  }
    527604 
     605  /**
     606    Clears all files that has been added to the
     607    {@link #ignore} set. All files in {@link #staticIgnore}
     608    are re-added.
     609  */
    528610  private void resetIgnore()
    529611  {
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/ExtensionsFile.java

    r4198 r4202  
    2525import java.io.File;
    2626import java.io.FileInputStream;
     27import java.io.FileNotFoundException;
    2728import java.io.FileOutputStream;
    2829import java.io.IOException;
     
    5657  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    5758*/
    58 public class ExtensionsFile
     59public class ExtensionsFile
     60  implements Comparable<ExtensionsFile>
    5961{
    6062  private static final org.apache.log4j.Logger log =
     
    6365  private final ExtensionsDirectory directory;
    6466  private final File file;
     67  private final File resourcesDir;
    6568  private final XmlLoader loader;
    6669  private final boolean isJar;
     
    6871  private boolean hasValidated;
    6972  private boolean isValid;
     73  private Throwable validationError;
    7074 
    7175  private long lastModified;
    7276  private long size;
    7377  private About about;
     78  private boolean hasError;
    7479 
    7580  /**
     
    7782    @param directory The directory with extensions;
    7883    @param file The XML or JAR file containting the extension definition
     84    @param resourcesDir The directory where resources are extracted
    7985    @param loader The XML loader to use
    8086  */
    81   public ExtensionsFile(ExtensionsDirectory directory, File file, XmlLoader loader)
     87  public ExtensionsFile(ExtensionsDirectory directory, File file,
     88    File resourcesDir, XmlLoader loader)
    8289  {
    8390    this.directory = directory;
    8491    this.file = file;
     92    this.resourcesDir = resourcesDir;
    8593    this.loader = loader;
    8694    this.isJar = file.getName().endsWith(".jar");
    8795  }
     96 
     97  /*
     98    From the Comparable interface
     99    -----------------------------
     100  */
     101  @Override
     102  public int compareTo(ExtensionsFile other)
     103  {
     104    return this.file.compareTo(other.file);
     105  }
     106  // ------------------------------
    88107 
    89108  /**
     
    138157  }
    139158 
     159  /**
     160    If the file is a new file not previously registered with the
     161    extension system. Thew 'new' status is changed when {@link #resetModified()}
     162    is called which usually happens when all extensions has been registered.
     163    @return TRUE if the file is a new file, FALSE otherwise
     164  */
    140165  public boolean isNew()
    141166  {
     
    153178   
    154179    @return TRUE if the file is valid, FALSE otherwise
     180    @see #getValidationError()
    155181  */
    156182  public boolean isValid()
     
    158184    if (!hasValidated || isModified())
    159185    {
    160       validate();
     186      try
     187      {
     188        validate();
     189      }
     190      catch (Exception ex)
     191      {}
    161192    }
    162193    return isValid;
     194  }
     195 
     196  /**
     197    Get more information about the error that caused the validation
     198    to fail.
     199    @return An exception or null if the validation succeeded
     200  */
     201  public Throwable getValidationError()
     202  {
     203    return validationError;
     204  }
     205 
     206  /**
     207    If there was an error when registering the extensions in this file.
     208    This property is only set if the file has been determined to be a
     209    valid extensions file by the {@link #isValid()} method. Extensions that
     210    has an error are automatically disabled.
     211   
     212    @return TRUE if there was some error, FALSE if everything is ok
     213  */
     214  public boolean hasError()
     215  {
     216    return hasError;
     217  }
     218 
     219  /**
     220    Sets the error status.
     221  */
     222  void setError(boolean error)
     223  {
     224    this.hasError = error;
    163225  }
    164226 
     
    215277  }
    216278 
     279  /**
     280    Load the extension definitions from the XML or JAR file.
     281  */
    217282  void loadExtensions()
    218283  {
     284    if (!loader.hasValidFile()) validate();
    219285    if (isJar)
    220286    {
    221       about = loadExtensionsJar();
     287      about = loadJar();
    222288    }
    223289    else
    224290    {
    225       about = loadExtensionsXml();
    226     }
    227   }
    228  
    229 
     291      about = loadXml();
     292    }
     293  }
     294 
    230295  private void validate()
    231296  {
    232297    hasValidated = true;
     298    isValid = false;
     299    validationError = null;
     300    try
     301    {
     302      if (isJar)
     303      {
     304        validateJar();
     305      }
     306      else
     307      {
     308        validateXml();
     309      }
     310    }
     311    catch (RuntimeException ex)
     312    {
     313      validationError = ex.getCause();
     314      throw ex;
     315    }
    233316    isValid = true;
    234317  }
    235318 
    236   private About loadExtensionsXml()
    237   {
     319  /**
     320    Try to validate an XML file with extensions. The XML file is valid
     321    if it matches the extension definition XML file format.
     322    @return The global about information or null if not present
     323  */
     324  private About validateXml()
     325  {
     326    log.info("Validating extensions in XML file: " + file.getName());
    238327    About about = null;
    239328    try
    240329    {
    241       about = loader.loadXmlFile(new FileInputStream(file),
    242         file.getAbsolutePath(), null, true);
     330      about = loader.validateXmlFile(new FileInputStream(file), file.getName());
    243331    }
    244332    catch (Exception ex)
    245333    {
    246       throw new RuntimeException(ex);
    247     }
     334      log.error("Error validating extensions in XML file: " + file, ex);
     335      throw new RuntimeException(file + " is not a valid extensions file", ex);
     336    }
     337    log.info("Extensions in XML file are valid: " + file.getName());
    248338    return about;
    249339  }
    250340 
    251   private About loadExtensionsJar()
    252   {
    253     log.info("Loading extension JAR file: " + file.getName());
     341  /**
     342    Try to validate a JAR file with extensions. The JAR file is valid
     343    if it contains a file 'META-INF/extensions.xml' which is a valid
     344    extension definition XML file.
     345    @return The global about information or null if not present
     346  */
     347  private About validateJar()
     348  {
     349    log.info("Validating extensions in JAR file: " + file.getName());
    254350    InputStream in = null;
    255351    ZipFile zipFile = null;
     
    257353    try
    258354    {
    259       ClassLoader jarLoader = JarClassLoader.getInstance(file.getAbsolutePath(), true);
    260355      zipFile = new ZipFile(file);
    261356      ZipEntry zipEntry = zipFile.getEntry("META-INF/extensions.xml");
     357      if (zipEntry == null)
     358      {
     359        throw new FileNotFoundException("META-INF/extensions.xml");
     360      }
    262361      in = zipFile.getInputStream(zipEntry);
    263       about = loader.loadXmlFile(in, file.getAbsolutePath(), jarLoader, true);
     362      about = loader.validateXmlFile(in, file.getName());
    264363    }
    265364    catch (Exception ex)
    266365    {
    267       log.error("Error loading extensions JAR file: " + file, ex);
    268       throw new RuntimeException(ex);
     366      log.error("Error validating extensions in JAR file: " + file, ex);
     367      throw new RuntimeException(file + " is not a valid extensions file", ex);
    269368    }
    270369    finally
     
    278377      {}
    279378    }
     379    log.info("Extensions in JAR file are valid: " + file.getName());
     380    return about;
     381  }
     382 
     383  /**
     384    Load extensions from a validated XML file.
     385    @return The global about information or null if not present
     386  */
     387  private About loadXml()
     388  {
     389    log.info("Loading extensions from XML file: " + file.getName());
     390    About about = null;
     391    try
     392    {
     393      about = loader.loadLastValidatedFile(null, true);
     394    }
     395    catch (Exception ex)
     396    {
     397      log.error("Error loading extensions from XML file: " + file, ex);
     398      throw new RuntimeException(ex);
     399    }
     400    log.info("Extensions loaded from XML file: " + file.getName());
     401    return about;
     402  }
     403 
     404  /**
     405    Load extensions from a validated JAR file.
     406    @return The global about information or null if not present
     407  */
     408  private About loadJar()
     409  {
     410    log.info("Loading extensions from JAR file: " + file.getName());
     411    About about = null;
     412    try
     413    {
     414      ClassLoader jarLoader = JarClassLoader.getInstance(file.getAbsolutePath(), true);
     415      about = loader.loadLastValidatedFile(jarLoader, true);
     416    }
     417    catch (Exception ex)
     418    {
     419      log.error("Error loading extensions from JAR file: " + file, ex);
     420      throw new RuntimeException(ex);
     421    }
     422    log.info("Extensions loaded from JAR file: " + file.getName());
    280423    return about;
    281424  }
     
    285428    if the file is an XML file.
    286429
    287     @param toDir The directory to extract the resources to
    288430    @param filter A regular expression that must match all files
    289431      that should be extracted, or null to every file
     
    295437    @return The number of extracted files
    296438  */
    297   int extractResources(File toDir, Pattern filter, String replacement, boolean forceOverwrite)
     439  int extractResources(Pattern filter, String replacement, boolean forceOverwrite)
    298440  {
    299441    if (!isJar) return 0;
    300442   
    301443    log.info("Extracting resources from " + file.getName() +
    302         " to " + toDir.getAbsolutePath() + "; forceOverwrite=" + forceOverwrite);
     444        " to " + resourcesDir.getAbsolutePath() + "; forceOverwrite=" + forceOverwrite);
    303445    log.debug("filter=" + filter + "; replacement=" + replacement);
    304446   
     
    307449    {
    308450      ZipInputStream zipStream = new ZipInputStream(new FileInputStream(file));
    309       ZipEntry zipEntry;
     451      ZipEntry zipEntry = null;
    310452      while ((zipEntry = zipStream.getNextEntry()) != null)
    311453      {
     
    348490        {
    349491          long time = zipEntry.getTime();
    350           File extractTo = new File(toDir, extractionName);
     492          File extractTo = new File(resourcesDir, extractionName);
    351493         
    352494          boolean modified = extractTo.lastModified() != time ||
     
    377519  }
    378520 
    379   int removeResources(File dir)
    380   {
    381     log.info("Removing resource files from " + dir.getAbsolutePath());
     521  /**
     522    Remove all extracted resources. This method simply removes all
     523    files and directories it can find in the home directory of the
     524    extensions. Files from two different extension packages should
     525    thus never be mixed in the same directory.
     526    @return The number of removed files
     527  */
     528  int removeResources()
     529  {
     530    log.info("Removing resource files from " + resourcesDir.getAbsolutePath());
    382531    int numRemoved = 0;
    383532   
    384533    List<File> directories = new ArrayList<File>();
    385     directories.add(dir);
     534    directories.add(resourcesDir);
    386535
    387536    while (directories.size() > 0)
     
    416565      }
    417566    }
    418     log.info("Removed " + numRemoved + " resource files from " + dir.getAbsolutePath());
     567    log.info("Removed " + numRemoved + " resource files from " + resourcesDir.getAbsolutePath());
    419568    return numRemoved;
    420569  }
    421570 
     571  /**
     572    Register all extensions with a registry.
     573    @param registry The registry
     574    @param forceUpdate TRUE to force an update of already registered
     575      extensions, FALSE to only update if the extension package has
     576      been modified
     577    @return The number of registered or updated extensions
     578  */
    422579  int registerExtensions(Registry registry, boolean forceUpdate)
    423580  {
    424581    for (Extension ext : loader.getExtensions())
    425582    {
    426       registry.setAttribute(ext.getId(), "FILE", this);
     583      directory.addRegistered(ext.getId(), this);
    427584    }
    428585    return loader.registerExtensions(registry, forceUpdate || isModified());
    429586  }
    430  
     587
     588  /**
     589    Register all extension points with a registry.
     590    @param registry The registry
     591    @param forceUpdate TRUE to force an update of already registered
     592      extension points, FALSE to only update if the extension package
     593      has been modified
     594    @return The number of registered or updated extensions
     595  */
    431596  int registerExtensionPoints(Registry registry, boolean forceUpdate)
    432597  {
    433598    for (ExtensionPoint ep : loader.getExtensionPoints())
    434599    {
    435       registry.setAttribute(ep.getId(), "FILE", this);
     600      directory.addRegistered(ep.getId(), this);
    436601    }
    437602    return loader.registerExtensionPoints(registry, forceUpdate || isModified());
    438603  }
    439604 
     605  /**
     606    Unregister extensions and extension points that have been removed
     607    from this package after an update.
     608 
     609    @param registry The registry
     610    @return The number of unregistered extensions and extension points
     611  */
     612  int unregisterMissing(Registry registry)
     613  {
     614    // TODO - implement
     615    return 0;
     616  }
     617 
     618  /**
     619    Unregister all extensions and extension points.
     620 
     621    @param registry The registry
     622    @return The number of unregistered extensions and extension points
     623  */
    440624  int unregisterAll(Registry registry)
    441625  {
     626    for (Extension ext : loader.getExtensions())
     627    {
     628      directory.removeRegistered(ext.getId(), this);
     629    }
     630    for (ExtensionPoint ep : loader.getExtensionPoints())
     631    {
     632      directory.removeRegistered(ep.getId(), this);
     633    }
    442634    int numExt = loader.unregisterExtensionPoints(registry);
    443635    numExt += loader.unregisterExtensions(registry);
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/JspContext.java

    r4187 r4202  
    11/**
    2   $Id$
     2  $Id:JspContext.java 4187 2008-03-20 11:15:25Z nicklas $
    33
    44  Copyright (C) Authors contributing to this file.
     
    2424package net.sf.basedb.clients.web.extensions;
    2525
    26 import java.io.IOException;
    27 import java.io.Writer;
     26import java.util.Collection;
    2827import java.util.HashSet;
    2928import java.util.Set;
    3029
     30import javax.servlet.jsp.JspWriter;
    3131import javax.servlet.jsp.PageContext;
    3232
     
    6565  @author nicklas
    6666  @version 2.7
    67   @base.modified $Date$
     67  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    6868*/
    6969public class JspContext
     
    7171{
    7272 
     73  private final ExtensionsDirectory directory;
     74  private final PageContext pageContext;
     75 
    7376  private Set<String> scripts;
    7477  private Set<String> stylesheets;
    7578 
    76   private Writer out;
    77   private PageContext pageContext;
    78  
    79   public JspContext(SessionControl sc, PageContext pageContext)
     79  JspContext(ExtensionsDirectory directory, SessionControl sc,
     80    PageContext pageContext)
    8081  {
    8182    super(sc);
     83    this.directory = directory;
    8284    this.pageContext = pageContext;
    8385  }
    84  
    85   public void setOut(Writer out)
    86   {
    87     this.out = out;
    88   }
    89  
    90   public Writer getOut()
    91   {
    92     return out;
    93   }
    94  
     86
     87  /**
     88    Get the JSP Page context object for the current request.
     89  */
    9590  public PageContext getPageContext()
    9691  {
    9792    return pageContext;
    9893  }
     94
     95  /**
     96    Get a writer object that can be used to return generated text
     97    to the response that is sent back to the browser.
     98    <p>
     99   
     100    Note! The writer is only intended to be used by render
     101    objects when rendering the extensions. Using the writer
     102    from the ActionFactory#prepareContext(Context, Extension)} or
     103    {@link RendererFactory#prepareContext(Context, Extension)}
     104    method may produce unpredictable results.
     105   
     106    @return A {@link JspWriter} object
     107  */
     108  public JspWriter getOut()
     109  {
     110    return pageContext.getOut();
     111  }
    99112 
    100  
     113  /**
     114    Get the URL to the web application root as a string. The URL
     115    only includes the path information, not the server name or protocol.
     116  */
    101117  public String getRoot()
    102118  {
     
    104120  }
    105121 
    106   // TODO
    107   public String getHome()
     122  /**
     123    Get the URL to the home directory for a given extension. The URL
     124    only includes the path information, not the server name or protocol.
     125    @param extension The extension
     126  */
     127  public String getHome(Extension extension)
    108128  {
    109     return "";
     129    ExtensionsFile file = directory.getFileByExtensionId(extension.getId());
     130    return file == null ? null : directory.getResourcesUrl(file);
    110131  }
    111132 
     
    158179  }
    159180
     181  /**
     182    Get all scripts that has been added to this context.
     183    @return A collection of scripts
     184  */
     185  public Collection<String> getScripts()
     186  {
     187    return scripts;
     188  }
    160189 
    161   public void writeScripts()
    162     throws IOException
     190  /**
     191    Get all stylesheets that has been added to this context.
     192    @return A collection of stylesheets
     193  */
     194  public Collection<String> getStylesheets()
    163195  {
    164     if (scripts == null) return;
    165     for (String script : scripts)
    166     {
    167       out.write("<script language=\"JavaScript\" type=\"text/javascript\" src=\"");
    168       out.write(script);
    169       out.write("\"></script>\n");
    170     }
     196    return stylesheets;
    171197  }
    172198 
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/ScanResults.java

    r4198 r4202  
    2525
    2626import java.util.Collection;
    27 import java.util.HashMap;
    2827import java.util.LinkedList;
    2928import java.util.List;
    3029import java.util.Map;
     30import java.util.TreeMap;
    3131
    3232/**
     
    6363    this.manualScan = manualScan;
    6464    this.forceUpdate = forceUpdate;
    65     this.fileResults = new HashMap<ExtensionsFile, FileResults>();
     65    this.fileResults = new TreeMap<ExtensionsFile, FileResults>();
    6666    this.hasError = false;
    6767    this.numErrorFiles = 0;
     
    167167 
    168168 
    169  
    170169  void setStatus(ExtensionsFile extFile, String status)
    171170  {
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/Settings.java

    r4198 r4202  
    6969  private static final String AUTO_INSTALL = "auto-install";
    7070
     71  private final ExtensionsDirectory directory;
    7172  private final File file;
    7273  private boolean hasChanged;
     
    8283    @param file The file to load/save settings in
    8384  */
    84   public Settings(File file)
     85  public Settings(ExtensionsDirectory directory, File file)
    8586  {
     87    this.directory = directory;
    8688    this.file = file;
    8789    this.presets = new Presets();
     
    113115  {
    114116    if (extensionPoint == null) return false;
     117    ExtensionsFile extFile = directory.getFileByExtensionId(extensionPoint.getId());
     118    if (extFile != null && (extFile.hasError() || !extFile.isValid())) return false;
    115119    return disabledExtensionPoints.getSetting(extensionPoint.getId()) == null;
    116120  }
     
    120124  {
    121125    if (extension == null) return false;
     126    ExtensionsFile extFile = directory.getFileByExtensionId(extension.getId());
     127    if (extFile != null && (extFile.hasError() || !extFile.isValid())) return false;
    122128    return disabledExtensions.getSetting(extension.getId()) == null;
    123129  }
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/toolbar/CompactButtonRenderer.java

    r4198 r4202  
    2828
    2929import net.sf.basedb.clients.web.extensions.JspContext;
     30import net.sf.basedb.clients.web.util.HTML;
    3031import net.sf.basedb.util.extensions.Renderer;
    3132
     
    4950  }
    5051 
     52  /**
     53    Generates a linked icon:
     54    <pre class="code">
     55<a
     56   id="[id]"
     57   class="[clazz]"
     58   style="[style]"
     59   href="javascript:[onClick]"
     60   title="[title]: [tooltip]"
     61><img src="[icon]" border="0"></a>
     62</pre>
     63
     64    If the button isn't visible nothing is generated. If the button isn't enabled,
     65    the href attribute is not generated.
     66  */
    5167  public void render(ButtonAction btn)
    5268  {
     69    if (!btn.isVisible()) return;
    5370    Writer out = context.getOut();
    5471    try
    5572    {
    56       out.write("<a href=\"javascript:");
    57       out.write(btn.getOnClick());
    58       out.write("\" title=\"");
    59       out.write(btn.getTitle());
    60       out.write("\"><img src=\"");
    61       out.write(btn.getIcon());
    62       out.write("\" border=\"0\"></a>\n");
     73      out.write("<a");
     74      if (btn.getId() != null) out.write(" id=\"" + btn.getId() + "\"");
     75      if (btn.getClazz() != null) out.write(" class=\"" + btn.getClazz() + "\"");
     76      if (btn.getStyle() != null) out.write(" style=\"" + btn.getStyle() + "\"");
     77      if (btn.isEnabled() && btn.getOnClick() != null)
     78      {
     79        out.write(" href=\"javascript:" + btn.getOnClick() + "\"");
     80      }
     81      String title = btn.getTitle();
     82      if (btn.getTooltip() != null)
     83      {
     84        title = title == null ? btn.getTooltip() : title + ": " + btn.getTooltip();
     85      }
     86      if (title != null) out.write(" title=\"" + HTML.encodeTags(title) + "\"");
     87      out.write("><img");
     88      if (btn.getIcon() != null) out.write(" src=\"" + btn.getIcon() + "\"");
     89      out.write(" border=\"0\"></a>\n");
    6390    }
    6491    catch (IOException ex)
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/toolbar/CompactButtonRendererFactory.java

    r4198 r4202  
    3030
    3131/**
     32  Factory that create {@link CompactButtonRenderer} objects
     33  for rendering {@link ButtonAction}:s.
    3234
    3335  @author nicklas
    3436  @version 2.7
    3537  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    36  */
     38*/
    3739public class CompactButtonRendererFactory
    3840  extends AbstractJspRendererFactory<ButtonAction>
     
    4244  {}
    4345 
     46  @Override
    4447  public CompactButtonRenderer getRenderer(Context context, Extension extension)
    4548  {
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/toolbar/FixedButtonFactory.java

    r4198 r4202  
    9191
    9292  /*
    93     From the ToolbarButtonAction interface
     93    From the ButtonAction interface
    9494    --------------------------------
    9595  */
  • trunk/src/core/net/sf/basedb/util/extensions/Registry.java

    r4198 r4202  
    2727import java.util.Collection;
    2828import java.util.Collections;
     29import java.util.Comparator;
    2930import java.util.HashMap;
    3031import java.util.Iterator;
     
    5657{
    5758
     59  private static final Comparator<ExtensionPoint<?>> EXTENSIONPOINT_COMPARATOR =
     60    new Comparator<ExtensionPoint<?>>()
     61    {
     62      @Override
     63      public int compare(ExtensionPoint<?> o1, ExtensionPoint<?> o2)
     64      {
     65        return o1.getName().compareTo(o2.getName());
     66      }
     67    };
     68 
    5869  private final Map<String, RegisteredExtensionPoint<?>> extensionPoints;
    5970  private final Map<String, RegisteredExtension<?>> extensions;
     
    192203    List<ExtensionPoint<?>> copy =
    193204      new ArrayList<ExtensionPoint<?>>(extensionPoints.values());
     205    Collections.sort(copy, EXTENSIONPOINT_COMPARATOR);
    194206    return Collections.unmodifiableList(copy).iterator();
    195207  }
    196  
    197208 
    198209  /**
     
    429440  */
    430441  private static final ExtensionsFilter DEFAULT_FILTER = new DefaultFilter();
    431    
     442 
    432443  /**
    433444    Internal representation of an extension point.
  • trunk/src/core/net/sf/basedb/util/extensions/xml/XmlLoader.java

    r4198 r4202  
    136136  private Map<Object, String> factoryParameters;
    137137 
     138  // The last document that was validated
     139  private Document validatedDom;
     140 
    138141  /**
    139142    Create a new XML loader instance.
     
    179182  /**
    180183    Load an extensions definition XML file. This method may be called
    181     multiple times with different XML files.
     184    multiple times with different XML files. The loading may also be
     185    done by a two-step process where the first step validates the file
     186    and the second step loads the extensions. See {@link
     187    #validateXmlFile(InputStream, String)} and {@link
     188    #loadLastValidatedFile(ClassLoader, boolean)}.
    182189
    183190    @param xmlFile An input stream to read the XML data from
     
    201208    @throws InstantiationException If a factory class can't be instantiated,
    202209      for example, because it is an abstract class or an interface
     210    @see #validateXmlFile(InputStream, String)
     211    @see #loadLastValidatedFile(ClassLoader, boolean)
    203212  */
    204213  public About loadXmlFile(InputStream xmlFile, String filename,
     
    207216      IllegalAccessException, InstantiationException
    208217  {
     218    validateXmlFile(xmlFile, filename);
     219    return loadLastValidatedFile(classLoader, clear);
     220  }
     221 
     222  /**
     223    Validate an XML file against the extensions definition schema. If the
     224    file is valid you may continue to load the extensions. See
     225    {@link #loadLastValidatedFile(ClassLoader, boolean)}.
     226   
     227    @param xmlFile An input stream to read the XML data from
     228    @param filename The original filename the stream is coming from, or null
     229      if not known. This value is only used when generating error messages
     230    @return Information about the extension, or null if no such information
     231      is available
     232    @throws JDOMException If validation of the XML file fails
     233    @throws IOException If there is an error reading the XML file
     234  */
     235  public About validateXmlFile(InputStream xmlFile, String filename)
     236    throws IOException, JDOMException
     237  {
     238    validatedDom = null;
     239    validatedDom = loadDocument(xmlFile, filename);
     240    About globalAbout = loadGlobalAbout(validatedDom);
     241    return globalAbout;
     242  }
     243 
     244  /**
     245    Continue loading extensions from the last validated XML file. This is
     246    the second step in a two-step process and requires that
     247    {@link #validateXmlFile(InputStream, String)} has been successfully called
     248    first.
     249
     250    @param classLoader The classloader to use when loading classes that are
     251      named in the XML file, or null to use the default classloader (=the same
     252      class loader that loaded the BASE core classes)
     253    @param clear TRUE to clear all already loaded extensions before loading
     254      the extensions in this file
     255    @return Information about the extension, or null if no such information
     256      is available
     257    @throws ClassNotFoundException If a class named in the XML file can't
     258      be found
     259    @throws NoSuchMethodException If a factory class doesn't implement a
     260      public, no-argument constructor
     261    @throws IllegalAccessException If a factory class constructor isn't
     262      public
     263    @throws InstantiationException If a factory class can't be instantiated,
     264      for example, because it is an abstract class or an interface
     265  */
     266  public About loadLastValidatedFile(ClassLoader classLoader, boolean clear)
     267    throws ClassNotFoundException, IllegalAccessException,
     268    InstantiationException, NoSuchMethodException
     269  {
     270    if (validatedDom == null)
     271    {
     272      throw new NullPointerException("No file has been validated");
     273    }
    209274    if (clear)
    210275    {
     
    212277      extensions.clear();
    213278    }
    214     Document dom = loadDocument(xmlFile, filename);
    215     About globalAbout = loadGlobalAbout(dom);
    216     loadExtensionPoints(dom, classLoader, globalAbout);
    217     loadExtensions(dom, classLoader, globalAbout);
     279    About globalAbout = loadGlobalAbout(validatedDom);
     280    loadExtensionPoints(validatedDom, classLoader, globalAbout);
     281    loadExtensions(validatedDom, classLoader, globalAbout);
     282    validatedDom = null;
    218283    return globalAbout;
     284  }
     285 
     286  /**
     287    Checks if an XML file has passed validation in the
     288    {@link #validateXmlFile(InputStream, String)} method. If so,
     289    the {@link #loadLastValidatedFile(ClassLoader, boolean)} can be called
     290    to continue loading the extensions.
     291    <p>
     292    Note that once the file has been loaded this flag is reset to
     293    FALSE.
     294   
     295    @return TRUE if a file has been validated, FALSE otherwise
     296  */
     297  public boolean hasValidFile()
     298  {
     299    return validatedDom != null;
    219300  }
    220301 
  • trunk/www/admin/extensions/details.jsp

    r4198 r4202  
    4343  import="net.sf.basedb.clients.web.extensions.ExtensionsFile"
    4444  import="net.sf.basedb.clients.web.extensions.ScanResults"
     45  import="net.sf.basedb.clients.web.extensions.ScanResults.FileResults"
    4546  import="net.sf.basedb.clients.web.formatter.FormatterFactory"
    4647  import="net.sf.basedb.util.formatter.Formatter"
     
    8586  ExtensionPoint<?> ep = null;
    8687  ExtensionsFile file = null;
     88  ExtensionsFile extFile = null;
     89  ExtensionsFile epFile = null;
     90
    8791  if (filename != null)
    8892  {
     
    102106    }
    103107    extensionPointId = ext.getExtends();
    104     //if (file == null) file = ec.getFileByExtensionId(extensionId);
     108    extFile = ec.getFileByExtensionId(extensionId);
    105109  }
    106110 
     
    112116      throw new ItemNotFoundException("ExtensionPoint[id=" + extensionPointId + "]");
    113117    }
    114     //if (file == null) file = ec.getFileByExtensionId(extensionPointId);
     118    epFile = ec.getFileByExtensionId(extensionPointId);
    115119  }
    116120%>
     
    177181    {
    178182      boolean isEnabled = ec.isEnabled(ext);
     183      boolean hasError = extFile != null && extFile.hasError();
     184      boolean allow = writePermission && !hasError;
    179185      %>
    180186      <tbl:button
    181187        onclick="<%="enableExtension(" + (isEnabled ? "0" : "1") + ")"%>"
    182188        title="<%=isEnabled ? "Disable" : "Enable" %>"
    183         image="<%=writePermission ? "joust/extension.png" : "joust/extensiondisabled.png" %>"
     189        image="<%=allow ? "joust/extension.png" : "joust/extensiondisabled.png" %>"
    184190        tooltip="Disable this extension"
    185         disabled="<%=!writePermission%>"
     191        disabled="<%=!allow%>"
    186192      />
    187193      <tbl:button
     
    196202    {
    197203      boolean isEnabled = ec.isEnabled(ep);
     204      boolean hasError = epFile != null && epFile.hasError();
     205      boolean allow = writePermission && !hasError;
    198206      %>
    199207      <tbl:button
    200208        onclick="<%="enableExtensionPoint(" + (isEnabled ? "0" : "1") + ")"%>"
    201209        title="<%=isEnabled ? "Disable" : "Enable" %>"
    202         image="<%=writePermission ? "joust/extensionpoint.png" : "joust/extensionpointdisabled.png" %>"
     210        image="<%=allow ? "joust/extensionpoint.png" : "joust/extensionpointdisabled.png" %>"
    203211        tooltip="Disable this extension point"
    204         disabled="<%=!writePermission%>"
     212        disabled="<%=!allow%>"
    205213      />
    206214      <tbl:button
     
    214222    else if (file != null)
    215223    {
     224      boolean hasError = file.hasError();
     225      boolean allow = writePermission && !hasError;
    216226      %>
    217227      <tbl:button
    218228        onclick="enableFile(1)"
    219229        title="Enable all"
    220         image="<%=writePermission ? "joust/extension.png" : "joust/extensiondisabled.png" %>"
     230        image="<%=allow ? "joust/extension.png" : "joust/extensiondisabled.png" %>"
    221231        tooltip="Enable all extensions in this file"
    222         disabled="<%=!writePermission%>"
     232        disabled="<%=!allow%>"
    223233      />
    224234      <tbl:button
    225235        onclick="enableFile(0)"
    226236        title="Disable all"
    227         image="<%=writePermission ? "joust/extension.png" : "joust/extensiondisabled.png" %>"
     237        image="<%=allow ? "joust/extension.png" : "joust/extensiondisabled.png" %>"
    228238        tooltip="Disable all extensions in this file"
    229         disabled="<%=!writePermission%>"
     239        disabled="<%=!allow%>"
    230240      />
    231241      <%
     
    266276      About about = ext.getAbout();
    267277      if (about == null) about = new AboutBean();
    268       ExtensionsFile extFile = ec.getFileByExtensionId(ext.getId());
    269278      %>
    270279      </a>
     
    282291          <td>
    283292            <a href="javascript:showFile('<%=HTML.javaScriptEncode(extFile.getName())%>')"
    284             ><%=extFile.getName()%></a> (<%=extFile.isModified() ? "Modified" : "Up to date" %>)
     293            ><%=extFile.getName()%></a>
     294            (<%=extFile.isModified() ? "Modified" : "Up to date" %>;
     295            <%=extFile.hasError() ? "Error" : "Ok" %>)
    285296          </td>
    286297        </tr>
     
    326337    if (ep != null)
    327338    {
    328       ExtensionsFile epFile = ec.getFileByExtensionId(ep.getId());
    329339      if (ext != null)
    330340      {
     
    347357          <td>
    348358            <a href="javascript:showFile('<%=HTML.javaScriptEncode(epFile.getName())%>')"
    349             ><%=epFile.getName()%></a> (<%=epFile.isModified() ? "Modified" : "Up to date" %>)
     359            ><%=epFile.getName()%></a>
     360            (<%=epFile.isModified() ? "Modified" : "Up to date" %>;
     361            <%=epFile.hasError() ? "Error" : "Ok" %>)
    350362          </td>
    351363        </tr>
     
    385397          <td class="prompt">Up to date</td>
    386398          <td><%=file.isModified() ? "No" : "Yes"%></td>
     399        </tr>
     400        <tr>
     401          <td class="prompt">Errors</td>
     402          <td>
     403            <%
     404            if (file.hasError())
     405            {
     406              ScanResults results = ec.getLastScanResults();
     407              FileResults fileResults = results.getResults(file);
     408              List<String> messages = fileResults.getMessages();
     409              Throwable validationError = file.getValidationError();
     410              for (String msg : messages)
     411              {
     412                %>
     413                <li><%=HTML.niceFormat(msg)%>
     414                <%
     415              }
     416              if (validationError != null)
     417              {
     418                %>
     419                <li><%=HTML.niceFormat(validationError.getMessage())%>
     420                <%
     421              }
     422            }
     423            else
     424            {
     425              %>
     426              No
     427              <%
     428            }
     429            %>
     430          </td>
    387431        </tr>
    388432        <tr>
  • trunk/www/admin/extensions/scan_results.jsp

    r4198 r4202  
    115115      List<String> messages = fileResults.getMessages();
    116116      boolean hasMessages = messages != null && messages.size() > 0;
     117      Throwable validationError = extFile.getValidationError();
    117118      %>
    118119      <tr>
     
    128129      </tr>
    129130      <%
    130       if (hasMessages)
     131      if (hasMessages || validationError != null)
    131132      {
    132133        %>
     
    138139            %>
    139140            <li><%=HTML.niceFormat(msg)%>
     141            <%
     142          }
     143          if (validationError != null)
     144          {
     145            %>
     146            <li><%=HTML.niceFormat(validationError.getMessage())%>
    140147            <%
    141148          }
  • trunk/www/admin/extensions/tree.jsp

    r4198 r4202  
    5050  if (name == null) name = id;
    5151  String icon = ec.isEnabled(ep) ? "ExtensionPoint" : "ExtensionPointDisabled";
     52  ExtensionsFile f = ec.getFileByExtensionId(id);
     53  if (f.hasError()) icon = "ExtensionPointError";
    5254  String joust = "ep = JoustMenu.addChildItem(" + parentNode +", '" + icon + "', " +
    5355      "'" + HTML.javaScriptEncode(name) + "', 'extensionPointOnClick(\"" + id + "\")', " +
     
    6163  String name = about == null || about.getName() == null ? id : about.getName();
    6264  String icon = ec.isEnabled(ext) ? "Extension" : "ExtensionDisabled";
     65  ExtensionsFile f = ec.getFileByExtensionId(id);
     66  if (f.hasError()) icon = "ExtensionError";
    6367  String joust = "ext = JoustMenu.addChildItem(" + parentNode + ", '" + icon + "', " +
    6468      "'" + HTML.javaScriptEncode(name) + "', 'extensionOnClick(\"" + id + "\")', " +
     
    9094    IconStore.addIcon('ExtensionPointDisabled', path + 'extensionpointdisabled.png', 16, 16);
    9195    IconStore.addIcon('ExtensionPointDisabledSelected', path + 'extensionpointdisabledselected.png', 16, 16);
     96    IconStore.addIcon('ExtensionPointError', path + 'extensionpointerror.png', 16, 16);
     97    IconStore.addIcon('ExtensionPointErrorSelected', path + 'extensionpointerrorselected.png', 16, 16);
    9298    IconStore.addIcon('Extension', path + 'extension.png', 16, 16);
    9399    IconStore.addIcon('ExtensionSelected', path + 'extensionselected.png', 16, 16);
    94100    IconStore.addIcon('ExtensionDisabled', path + 'extensiondisabled.png', 16, 16);
    95101    IconStore.addIcon('ExtensionDisabledSelected', path + 'extensiondisabledselected.png', 16, 16);
     102    IconStore.addIcon('ExtensionError', path + 'extensionerror.png', 16, 16);
     103    IconStore.addIcon('ExtensionErrorSelected', path + 'extensionerrorselected.png', 16, 16);
    96104    IconStore.addIcon('XmlFile', path + 'item.gif', 18, 16);
     105    IconStore.addIcon('XmlFileError', path + 'itemerror.gif', 18, 16);
    97106    IconStore.addIcon('XmlFileSelected', path + 'itemselected.gif', 18, 16);
     107    IconStore.addIcon('XmlFileErrorSelected', path + 'itemerrorselected.gif', 18, 16);
    98108    IconStore.addIcon('JarFile', path + 'jarfile.png', 18, 16);
     109    IconStore.addIcon('JarFileError', path + 'jarfileerror.png', 18, 16);
    99110    IconStore.addIcon('JarFileSelected', path + 'jarfileselected.png', 18, 16);
     111    IconStore.addIcon('JarFileErrorSelected', path + 'jarfileerrorselected.png', 18, 16);
    100112
    101113    var byExtPoint = JoustMenu.addMenuItem(-1, 'Root', 'By extension point', 'rootOnClick()', '', '');
     
    130142      String efName = ef.getName();
    131143      String icon = ef.isJar() ? "JarFile" : "XmlFile";
     144      if (ef.hasError()) icon += "Error";
    132145      String id = efName;
    133146      %>
  • trunk/www/include/menu.jsp

    r4190 r4202  
    952952    <%
    953953    // Extensions menu
    954     JspContext context = new JspContext(sc, pageContext);
     954    JspContext context = ExtensionsControl.createContext(sc, pageContext);
    955955    ExtensionsInvoker<MenuItemAction> invoker =
    956956      (ExtensionsInvoker<MenuItemAction>)ExtensionsControl.useExtensions(context,
  • trunk/www/views/experiments/bioassaysets/analysis_tree.jsp

    r4190 r4202  
    7979<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
    8080<%@ taglib prefix="tbl" uri="/WEB-INF/table.tld" %>
    81 <%@ taglib prefix="t" uri="/WEB-INF/tab.tld" %>
    82 <%@ taglib prefix="p" uri="/WEB-INF/path.tld" %>
     81<%@ taglib prefix="ext" uri="/WEB-INF/extensions.tld" %>
    8382<%!
    8483  private static final Item itemType = Item.BIOASSAYSET;
     
    280279  int numListed = 0;
    281280 
    282   JspContext jspContext = new JspContext(sc, pageContext);
    283   ExtensionsInvoker<ButtonAction> invoker =
    284     (ExtensionsInvoker<ButtonAction>)ExtensionsControl.useExtensions(jspContext, "net.sf.basedb.clients.web.bioassayset.list.tools");
     281  JspContext jspContext = ExtensionsControl.createContext(sc, pageContext);
     282  ExtensionsInvoker invoker = ExtensionsControl.useExtensions(jspContext,
     283      "net.sf.basedb.clients.web.bioassayset.list.tools");
    285284  %>
    286285<base:page type="include">
    287286  <base:body>
    288     <%
    289       jspContext.setOut(out);
    290       jspContext.writeScripts();
    291     %>
     287    <ext:scripts context="<%=jspContext%>" />
     288    <ext:stylesheets context="<%=jspContext%>" />
    292289    <script language="JavaScript">
    293290    var submitPage = '<%=transformationId != 0 ? "../bioassaysets/index.jsp" : "index.jsp"%>';
     
    887884                      }
    888885                      %>
    889                       <%
    890                       jspContext.setCurrentItem(item);
    891                       jspContext.setOut(out);
    892                       invoker.renderDefault();
    893                       %>
    894 
     886                      <ext:render extensions="<%=invoker%>" context="<%=jspContext%>" item="<%=item%>" />
    895887                      </nobr>
    896888                    </tbl:cell>
     
    949941                      }
    950942                      %>
    951                       <%
    952                       jspContext.setCurrentItem(item);
    953                       jspContext.setOut(out);
    954                       invoker.renderDefault();
    955                       %>
     943                      <ext:render extensions="<%=invoker%>" context="<%=jspContext%>" item="<%=item%>" />
    956944                      </nobr>
    957945                    </tbl:cell>
     
    975963                      }
    976964                      %>
    977                       <%
    978                       jspContext.setCurrentItem(item);
    979                       jspContext.setOut(out);
    980                       invoker.renderDefault();
    981                       %>
    982                            
     965                      <ext:render extensions="<%=invoker%>" context="<%=jspContext%>" item="<%=item%>" />                           
    983966                      </nobr>
    984967                    </tbl:cell>
Note: See TracChangeset for help on using the changeset viewer.