Changeset 4198


Ignore:
Timestamp:
Mar 28, 2008, 2:41:14 PM (15 years ago)
Author:
Nicklas Nordborg
Message:

References #436: Create extension system for the web application

Major code and documentation cleanup of core classes. Added lots of more administrative functions. Automatic and manual installation is enabled.

Location:
trunk
Files:
6 added
3 deleted
31 edited

Legend:

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

    r4187 r4198  
    2424package net.sf.basedb.clients.web.extensions;
    2525
    26 import java.util.HashSet;
    27 import java.util.Set;
    28 
    2926import net.sf.basedb.util.extensions.Action;
    3027import net.sf.basedb.util.extensions.ActionFactory;
    3128import net.sf.basedb.util.extensions.Context;
    3229import net.sf.basedb.util.extensions.Extension;
    33 import net.sf.basedb.util.extensions.xml.PathSetter;
    34 import net.sf.basedb.util.extensions.xml.VariableSetter;
    3530
    3631/**
    3732  An abstract base class for action factories that may need
    3833  to support adding scripts and stylesheets to the rendered
    39   JSP page.
    40  
    41   <p>
    42   Use the {@link #setScript(String)} and {@link #setStylesheet(String)}
    43   methods to add references to script and stylesheet files to the factory.
    44 
    45   <p>
    46   Note! The naming
    47   of these methods may be a bit misleading, since each call ADD the argument
    48   to a set. To remove items from the sets use the {@link #getScripts()} and
    49   {@link #getStylesheets()} methods.
     34  JSP page. Most of this functionality is inherited from the
     35  {@link AbstractJspFactory} class.
    5036 
    5137  <p>
    5238  Scripts and stylesheets that has been added to the factory will
    53   be propagated to the {@link JspContext#addScripts(java.util.Collection)}
    54   and {@link JspContext#addStylesheets(java.util.Collection)} methods in
    55   the {@link #prepareContext(Context)}.
     39  be propagated to the {@link JspContext#addScript(String)}
     40  and {@link JspContext#addStylesheet(String)} methods in
     41  the {@link #prepareContext(Context, Extension)}.
    5642 
    5743  @author nicklas
    5844  @version 2.7
    59   @base.modified $Date$
     45  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    6046*/
    61 public abstract class AbstractJspActionFactory<A extends Action>
     47public abstract class AbstractJspActionFactory<A extends Action>
     48  extends AbstractJspFactory
    6249  implements ActionFactory<A>
    6350{
    64 
    65   private Set<String> scripts;
    66   private Set<String> stylesheets;
    6751 
    6852  /**
     
    7862  /**
    7963    If scripts and stylesheets has been added to the factory,
    80     propagates those to the {@link JspContext}. A subclass that overrides
    81     this method should call <code>super.prepareContext()</code>
    82     if it hasn't decided to return FALSE from this method.
     64    propagates those to the {@link JspContext}. A subclass that
     65    overrides this method should call <code>super.prepareContext()</code>
     66    if it hasn't decided to return FALSE already.
    8367    @return Always TRUE
    8468  */
     
    8670  public boolean prepareContext(Context context, Extension<? super A> extension)
    8771  {
    88     JspContext jspContext = (JspContext)context;
    89     if (scripts != null)
    90     {
    91       for (String script : scripts)
    92       {
    93         jspContext.addScript(script);
    94       }
    95     }
    96     if (stylesheets != null)
    97     {
    98       for (String stylesheet : stylesheets)
    99       {
    100         jspContext.addStylesheet(stylesheet);
    101       }
    102     }
     72    prepareContext((JspContext)context);
    10373    return true;
    10474  }
    10575  // ----------------------------------
    106  
    107   /**
    108     Add a reference to a javascript file to the factory.
    109     @param script The script to add
    110   */
    111   @VariableSetter
    112   @PathSetter
    113   public void setScript(String script)
    114   {
    115     if (scripts == null) scripts = new HashSet<String>();
    116     scripts.add(script);
    117   }
    118  
    119   @VariableSetter
    120   @PathSetter
    121   public void setStylesheet(String stylesheet)
    122   {
    123     if (stylesheets == null) stylesheets = new HashSet<String>();
    124     stylesheets.add(stylesheet);
    125   }
    126  
    127   public Set<String> getScripts()
    128   {
    129     return scripts;
    130   }
    131  
    132   public Set<String> getStylesheets()
    133   {
    134     return stylesheets;
    135   }
    13676
    137  
    13877}
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/ExtensionsControl.java

    r4187 r4198  
    2323package net.sf.basedb.clients.web.extensions;
    2424
     25import java.io.File;
    2526import java.util.Collections;
    2627import java.util.Iterator;
     
    4344/**
    4445  Controller class for the extension system in the web client. This is the
    45   main class to use when working with extensions. To get all registered
    46   extensions for an extension point call {@link #useExtensions(JspContext, String...)}.
     46  main class to use when working with extensions from the JSP code.
    4747  <p>
     48 
     49  To get all registered extensions for an extension point call
     50  {@link #useExtensions(JspContext, String...)}.
     51  <p>
     52
    4853  Use {@link #get(DbControl)} to access the management API wich allows
    4954  you to enable/disable extensions and change settings for the extension
     
    5257  @author nicklas
    5358  @version 2.7
    54   @base.modified $Date$
     59  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    5560*/
    5661public class ExtensionsControl
     
    6267 
    6368  /**
    64     The location where extensions should be installed.
     69    The location where the extension system is looking for extensions.
    6570  */
    6671  public static final String EXTENSIONS_URL = "/WEB-INF/extensions";
     
    7883 
    7984  // The directory where extension files are located
    80   private static ExtensionsDirectory extensions;
     85  private static ExtensionsDirectory extensionsDir;
    8186 
    8287  // Settings
     
    8691  private static TimerTask autoInstallTask;
    8792 
     93  // The result of the last automatic update
     94  private static ScanResults lastScanResults;
     95 
     96  // The scheduled time of the next automatic scan, or 0 if not enabled
     97  private static long nextAutoScan;
     98 
    8899  /**
    89100    Initialise the extension system. This method is called once from the
    90101    {@link ExtensionsServlet} when the web server starts up.
    91102  */
    92   public static synchronized void init(Registry reg, ExtensionsDirectory ext, Settings set)
     103  public static synchronized void init(ExtensionsDirectory directory)
    93104  {
    94105    if (initialised) return;
    95106    log.info("Initialising extensions controller");
    96     registry = reg;
    97     extensions = ext;
    98     settings = set;
    99     extensions.installAndUpdateExtensions(registry, false);
     107    extensionsDir = directory;
     108   
     109    // Create registry
     110    registry = new Registry();
     111   
     112    // Load settings
     113    File settingsFile = new File(directory.getExtensionDirectory(), "settings.xml");
     114    settings = new Settings(settingsFile);
     115    extensionsDir.addIgnore(settingsFile);
     116   
     117    // Install extensions
     118    lastScanResults = extensionsDir.installAndUpdateExtensions(registry, false, false);
    100119    initAutoInstaller();
    101120    initialised = true;
     
    117136    }
    118137    int autoInstall = settings.getAutoInstall();
    119     String finalInfoMessage = "";
    120138    if (autoInstall > 0)
    121139    {
     
    123141      long checkInterval = autoInstall * 1000;
    124142      autoInstallTask = Application.getScheduler().schedule(new AutoInstallTask(), checkInterval, checkInterval, false);
    125       finalInfoMessage = "Auto-installation system is running with " + autoInstall + " seconds interval";
     143      nextAutoScan = System.currentTimeMillis() + checkInterval;
     144      log.info("Auto-installation system is running with " + autoInstall + " seconds interval");
    126145    }
    127146    else
     
    129148      log.debug("Disabling auto-installation system (autoInstall=" + autoInstall + ")");
    130149      autoInstallTask = null;
    131       finalInfoMessage = "Auto-installation system is now disabled";
    132     }
    133     log.info(finalInfoMessage);
     150      nextAutoScan = 0;
     151      log.info("Auto-installation system is now disabled");
     152    }
    134153  }
    135154 
     
    138157    installed extensions. Everybody has permission to
    139158    read information. To update, WRITE permission for the
    140     current web client is needed.
     159    web client application is needed.
    141160   
    142161    @param dc The DbControl to use for database access and permission checks
     
    200219  // ----------------------------------------------
    201220 
     221  /**
     222    For checking the permission and adding a custom message to
     223    the PermissionDeniedException if the required permission is
     224    missing.
     225  */
    202226  private void checkPermission(Permission permission, String what)
    203227    throws PermissionDeniedException
     
    220244      have WRITE permission
    221245  */
    222   public void installAndUpdateExtensions(boolean forceUpdate)
     246  public ScanResults installAndUpdateExtensions(boolean forceUpdate)
    223247  {
    224248    checkPermission(Permission.WRITE, "extensions");
    225     extensions.installAndUpdateExtensions(registry, forceUpdate);
     249    lastScanResults =
     250      extensionsDir.installAndUpdateExtensions(registry, true, forceUpdate);
     251    return lastScanResults;
    226252  }
    227253 
     
    243269    @param autoInstall Number of seconds between automatic checks,
    244270      or 0 or negative to disable
     271    @throws PermissionDeniedException If the logged in user doesn't
     272      have WRITE permission
    245273    @see #getAutoInstall()
    246274  */
     
    251279    initAutoInstaller();
    252280  }
     281
     282  /**
     283    Get the results of the last scan (manual or automatic).
     284    @return A {@link ScanResults} object, or null if no scan
     285      has taken place
     286  */
     287  public ScanResults getLastScanResults()
     288  {
     289    return lastScanResults;
     290  }
     291 
     292  /**
     293    Get the next scheduled time for automatic scanning. Note!
     294    It is not guaranteed that the next automatic scan will take
     295    place at exactly the returned time. Depending on timer resolution
     296    and other activity the scan may take place sooner or later.
     297   
     298    @return A time value in milliseconds or 0 if automatic
     299      scanning is disabled
     300    @see System#currentTimeMillis()
     301  */
     302  public long getNextAutoScanTime()
     303  {
     304    return nextAutoScan;
     305  }
    253306 
    254307  /**
     
    260313    return registry.getExtensionPoints();
    261314  }
    262 
    263315 
    264316  /**
     
    354406  }
    355407
     408  /**
     409    Get an iterator returning all XML/JAR files which contains installed
     410    extensions.
     411  */
     412  public Iterator<ExtensionsFile> getFiles()
     413  {
     414    return extensionsDir.getFiles();
     415  }
     416 
     417  /**
     418    Get information about an installed extensions file.
     419    @param filename The filename of the file
     420    @return An {@link ExtensionsFile} object or null if
     421      the given file doesn't exists or isn't an extensions file
     422  */
     423  public ExtensionsFile getFile(String filename)
     424  {
     425    return extensionsDir.getFile(filename);
     426  }
     427 
     428  /**
     429    Get information about the file a given extension or extension
     430    point is defined in.
     431    @param extensionId The ID of an extension or extension point
     432    @return Information about the file the extension is defined in,
     433      or null if no extension with the given ID is found
     434  */
     435  public ExtensionsFile getFileByExtensionId(String extensionId)
     436  {
     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;
     444  }
     445 
     446  /**
     447    Enable or disable all extensions and extension points in a file.
     448    If no file with the given name is found or if that file isn't an
     449    extensions file, nothing is done.
     450    @param filename The filename which contains the extensions
     451    @param enable TRUE to enable, FALSE to disable
     452  */
     453  public void enableAllInFile(String filename, boolean enable)
     454  {
     455    checkPermission(Permission.WRITE, "File[" + filename + "]");
     456    ExtensionsFile file = getFile(filename);
     457    if (file == null) return;
     458   
     459    Iterator<ExtensionPoint<?>> itp = file.getExtensionPoints();
     460    while (itp.hasNext())
     461    {
     462      ExtensionPoint<?> ep = itp.next();
     463      settings.enableExtensionPoint(ep.getId(), enable);
     464    }
     465
     466    Iterator<Extension<?>> itx = file.getExtensions();
     467    while (itx.hasNext())
     468    {
     469      Extension<?> ext = itx.next();
     470      settings.enableExtension(ext.getId(), enable);
     471    }
     472  }
     473 
    356474  /**
    357475    Save the settings. This method should be called when changes
     
    384502    {
    385503      log.debug("Checking for new/updated/deleted extensions");
    386       extensions.installAndUpdateExtensions(registry, false);
     504      int autoInstall = settings.getAutoInstall();
     505      lastScanResults = extensionsDir.installAndUpdateExtensions(registry, false, false);
     506      nextAutoScan = autoInstall > 0 ?
     507        System.currentTimeMillis() + autoInstall * 1000L : 0;
    387508      log.debug("Done checking for new/updated/deleted extensions.");
    388509    }
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/ExtensionsDirectory.java

    r4187 r4198  
    11/**
    2   $Id$
     2  $Id:ExtensionsDirectory.java 4187 2008-03-20 11:15:25Z nicklas $
    33
    44  Copyright (C) Authors contributing to this file.
     
    2626import java.io.File;
    2727import java.io.FileFilter;
     28import java.util.ArrayList;
     29import java.util.Collections;
    2830import java.util.HashMap;
    2931import java.util.HashSet;
    3032import java.util.Iterator;
     33import java.util.List;
    3134import java.util.Map;
    3235import java.util.Set;
     
    4447  are added, changed or removed from the specified directory.
    4548  Resources, such as images, JSP files, etc. are automatically extracted
    46   from JAR files and placed in the resources directory. This directory is
    47   accessible from the web server.
     49  from JAR files and placed in the resources directory, which is a
     50  directory that should be accessible from the web server.
    4851
    4952  @author nicklas
    5053  @version 2.7
    51   @base.modified $Date$
     54  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    5255*/
    5356public class ExtensionsDirectory
     
    6265  private final String rootUrl;
    6366 
    64   private final Map<File, ExtensionsFile> installedExtensions;
    65   private final Set<ExtensionsFile> ignore;
     67  private final Map<File, ExtensionsFile> installedFiles;
     68  private final Map<String, ExtensionsFile> installedExtensions;
     69  private final Set<File> staticIgnore;
     70  private final Set<File> ignore;
    6671  private final VariableConverter variableConverter;
    6772  private final PathConverter pathConverter;
     
    7580    this.rootUrl = rootUrl;
    7681   
    77     this.installedExtensions = new HashMap<File, ExtensionsFile>();
    78     this.ignore = new HashSet<ExtensionsFile>();
    79     ignore.add(new ExtensionsFile(new File(extensionsDir, "settings.xml"), null));
     82    this.installedFiles = new HashMap<File, ExtensionsFile>();
     83    this.installedExtensions = new HashMap<String, ExtensionsFile>();
     84    this.staticIgnore = new HashSet<File>();
     85    this.ignore = new HashSet<File>();
    8086    this.variableConverter = new VariableConverter();
    8187    this.variableConverter.setVariable("ROOT", rootUrl);
     
    8490  }
    8591 
    86   public synchronized void installAndUpdateExtensions(Registry registry, boolean forceUpdate)
     92  /**
     93    Get the file object representing the directory where
     94    extensions are installed.
     95    @return A file object
     96  */
     97  public File getExtensionDirectory()
     98  {
     99    return extensionsDir;
     100  }
     101 
     102  /**
     103    Get the file object representing the directory where
     104    resource files are extracted.
     105    @return A file object
     106  */
     107  public File getResourcesDirectory()
     108  {
     109    return resourcesDir;
     110  }
     111 
     112  /**
     113    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
     116    @return The URL
     117  */
     118  public String getResourcesUrl()
     119  {
     120    return resourcesUrl;
     121  }
     122 
     123  /**
     124    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
     127    @return The URL
     128  */
     129  public String getRootUrl()
     130  {
     131    return rootUrl;
     132  }
     133 
     134  /**
     135    Add a file to the ignore list. Files in this list will never
     136    be checked for extensions again. This is only neccessary for XML/JAR
     137    files that doesn't contain extensions.
     138    @param file The file to add
     139    @see #removeIgnore(File)
     140  */
     141  public void addIgnore(File file)
     142  {
     143    ignore.add(file);
     144    staticIgnore.add(file);
     145  }
     146 
     147  /**
     148    Remove a file from the ignore list.
     149    @param file The file to remove
     150    @see #addIgnore(File)
     151  */
     152  public void removeIgnore(File file)
     153  {
     154    ignore.remove(file);
     155    staticIgnore.remove(file);
     156  }
     157 
     158  /**
     159    Get an iterator returning all XML/JAR files which contains installed
     160    extensions.
     161  */
     162  public Iterator<ExtensionsFile> getFiles()
     163  {
     164    // Create a copy, or the iterator will fail if new
     165    // files are added by another thread
     166    List<ExtensionsFile> copy =
     167      new ArrayList<ExtensionsFile>(installedFiles.values());
     168    return Collections.unmodifiableList(copy).iterator();
     169  }
     170 
     171  /**
     172    Get information about an installed extensions file.
     173    @param filename The filename of the file
     174    @return An {@link ExtensionsFile} object or null if
     175      the given file doesn't exists or isn't an extensions file
     176  */
     177  public ExtensionsFile getFile(String filename)
     178  {
     179    return installedFiles.get(new File(extensionsDir, filename));
     180  }
     181 
     182  /**
     183    Scan the directory for new/modified/deleted extension files
     184    and perform neccesary changed in the registry.
     185
     186    @param registry The registry holding registered extension points
     187      and extensions
     188    @param manualScan TRUE if this is a manual scan, FALSE if it is an automatic
     189    @param forceUpdate TRUE to force an update of unmodified extensions,
     190      FALSE to leave them as they are
     191    @return The result of the scan
     192  */
     193  public synchronized ScanResults installAndUpdateExtensions(Registry registry,
     194    boolean manualScan, boolean forceUpdate)
    87195  {
    88196    log.info("Begin scan for new/updated/deleted extensions");
    89     if (forceUpdate) ignore.clear();
    90     int numDeleted = unregisterDeleted(registry);
    91     int numNewOrUpdated = scanForNewAndUpdated();
    92    
    93     if (numNewOrUpdated == 0 && !forceUpdate) return;
    94     loadDefinitions(forceUpdate);
    95     extractResources(forceUpdate);
    96     registerExtensions(registry);
     197    ScanResults results = new ScanResults(manualScan, forceUpdate);
     198    int numDeleted = 0;
     199    int numNew = 0;
     200    int numUpdated = 0;
     201    int numForced = 0;
     202    try
     203    {
     204      // Re-scan failed JAR/XML files; Doesn't affect manually added ignore files
     205      if (forceUpdate) resetIgnore();
     206     
     207      // 1. Unregister all extensions that no longer exists
     208      // This method also deletes extracted resources
     209      numDeleted = unregisterDeleted(registry, results);
     210     
     211      // 2. Find new and modified extensions
     212      int[] numNewAndUpdated = scanForNewAndUpdated(forceUpdate, results);
     213      numNew = numNewAndUpdated[0];
     214      numUpdated = numNewAndUpdated[1];
     215      numForced = numNewAndUpdated[2];
     216     
     217      // Do we need to do anything?
     218      if (numNew > 0 || numUpdated > 0 || numForced > 0)
     219      {
     220        // 3. Load new and modified definitions
     221        loadDefinitions(forceUpdate, results);
     222       
     223        // 4. Extract resources
     224        extractResources(forceUpdate, results);
     225       
     226        // 5. Register new and updated extensions with the registry
     227        registerExtensions(registry, forceUpdate, results);
     228      }
     229    }
     230    finally
     231    {
     232      // Generate summary message, etc.
     233      String summary = numDeleted + " deleted extensions\n" +
     234        numNew + " new extensions\n" +
     235        (numUpdated + numForced) + " updated extensions";
     236      if (results.getNumErrorFiles() > 0)
     237      {
     238        summary += "\n" + results.getNumErrorFiles() + " errors";
     239      }
     240      results.setSummary(summary);
     241      results.setEnded();
     242    }
    97243    log.info("End scan for new/updated/deleted extensions");
     244    return results;
    98245  }
    99246
    100247  /**
    101248    Unregister extensions/extension points that no longer
    102     exists.
    103   */
    104   private int unregisterDeleted(Registry registry)
     249    exists. This method will also delete extracted resources.
     250  */
     251  private int unregisterDeleted(Registry registry, ScanResults results)
    105252  {
    106253    log.info("Checking for deleted extensions in directory: " +
     
    108255    // Unregister extensions that have been deleted
    109256    int numDeleted = 0;
    110     Iterator<ExtensionsFile> it = installedExtensions.values().iterator();
     257    int numError = 0;
     258    Iterator<ExtensionsFile> it = installedFiles.values().iterator();
    111259    while (it.hasNext())
    112260    {
     
    117265      {
    118266        log.info("Unregistering extensions from " + extFile.getName());
    119         extFile.unregisterAll(registry);
    120         File homeDir = new File(resourcesDir, extFile.getName());
    121         extFile.removeResources(homeDir);
    122         it.remove();
    123         numDeleted++;
     267        try
     268        {
     269          // Unregister the extensions
     270          int numExtensions = extFile.unregisterAll(registry);
     271
     272          // Remove extracted resources
     273          File homeDir = new File(resourcesDir, extFile.getName());
     274          int numResources = extFile.removeResources(homeDir);
     275         
     276          // Set scan result messages
     277          results.setStatus(extFile, "Deleted");
     278          results.addMessage(extFile, numExtensions + " extensions unregisterered.");
     279          results.addMessage(extFile, numResources + " resources deleted.");
     280
     281          it.remove();
     282          numDeleted++;
     283        }
     284        catch (Exception ex)
     285        {
     286          results.addErrorMessage(extFile,
     287              "Could not unregister extensions: " + ex.getMessage());
     288          log.error("Could not unregister " + extFile.getName(), ex);
     289          numError++;
     290        }
    124291      }
    125292    }
    126293    log.info(numDeleted + " extensions has been deleted from directory: " +
    127294      extensionsDir.getAbsolutePath());
     295    if (numError > 0)
     296    {
     297      log.warn(numError + " extensions could not be deleted from directory: " +
     298        extensionsDir.getAbsolutePath());
     299    }
    128300    return numDeleted;
    129301  }
     
    131303  /**
    132304    Scan for new extensions. This method checks in the
    133     extensions directory for *.xml and *.jar files.
    134   */
    135   private int scanForNewAndUpdated()
     305    extensions directory for *.xml and *.jar files. New files
     306    will be validated and added to the {@link #installedFiles} map
     307    if valid or to the {@link #ignore} set if not valid. Files already
     308    in the {@link #ignore} set are not checked.
     309   
     310    @return [0] = num new; [1] = num updated; [2] = num forced update
     311  */
     312  private int[] scanForNewAndUpdated(boolean forceUpdate, ScanResults results)
    136313  {
    137314    log.info("Scanning for new extensions in directory: " +
     
    142319    int numNew = 0;
    143320    int numUpdated = 0;
     321    int numForced = 0;
    144322    for (File file : extensionsDir.listFiles(filter))
    145323    {
     
    147325      if (ignore.contains(file)) continue;
    148326     
    149       // Ignore files that we already know about
    150       if (!installedExtensions.containsKey(file))
    151       {
    152         log.debug("Possible match: " + file.getName());
     327      // Is this a known file or not?
     328      if (!installedFiles.containsKey(file))
     329      {
     330        log.debug("Found new file: " + file.getName());
    153331        ExtensionsFile extFile =
    154           new ExtensionsFile(file, createXmlLoader());
     332          new ExtensionsFile(this, file, createXmlLoader());
     333       
     334        // Is is a valid extensions file?
    155335        if (extFile.isValid())
    156336        {
    157           // This is a new valid extensions file
    158           log.info("Found new extensions in file: " + file.getName());
    159           installedExtensions.put(file, extFile);
     337          // This is a new valid extensions file; add it
     338          log.info("Found extensions in file: " + file.getName());
     339          installedFiles.put(file, extFile);
    160340          numNew++;
     341         
     342          // Set scan result messages -- details will be added later
     343          results.setStatus(extFile, "Installed");
    161344        }
    162345        else
    163346        {
    164           ignore.add(extFile);
     347          // Not a valid extensions file; add to ignore list
     348          log.info("Not a valid extensions file: " + file.getName());
     349          ignore.add(file);
     350         
     351          // Set scan result messages
     352          results.setStatus(extFile, "Ignored");
     353          results.addMessage(extFile, "Not a valid extensions XML or JAR file");
    165354        }
    166355      }
    167356      else
    168357      {
    169         ExtensionsFile extFile = installedExtensions.get(file);
     358        // This is a known file, has it been modified?
     359        ExtensionsFile extFile = installedFiles.get(file);
    170360        if (extFile.isModified())
    171361        {
    172362          log.debug("File has been updated: " + file.getName());
     363          results.setStatus(extFile, "Updated");
    173364          numUpdated++;
    174365        }
     366        else if (forceUpdate)
     367        {
     368          log.debug("File is already forced update: " + file.getName());
     369          results.setStatus(extFile, "Forced update");
     370          numForced++;
     371        }
    175372        else
    176373        {
    177374          log.debug("File is already installed: " + file.getName());
     375          results.setStatus(extFile, "Already installed");
    178376        }
    179377      }
     
    183381    log.info(numUpdated + " updated extensions found in directory: " +
    184382      extensionsDir.getAbsolutePath());
    185     return numNew + numUpdated;
    186   }
    187  
    188   /**
    189     Load the extension definitions from XML files.
    190   */
    191   private void loadDefinitions(boolean forceUpdate)
     383    log.info(numForced + " force updated extensions found in directory: " +
     384        extensionsDir.getAbsolutePath());
     385    return new int[] { numNew, numUpdated , numForced };
     386  }
     387 
     388  /**
     389    Load the extension definitions from XML files. Unless forceUpdate
     390    is TRUE only new or modified extensions will be loaded.
     391  */
     392  private void loadDefinitions(boolean forceUpdate, ScanResults results)
    192393  {
    193394    log.info("Loading new or updated extension definitions from: " +
    194395        extensionsDir.getAbsolutePath());
    195396    log.debug("forceUpdate=" + forceUpdate);
    196     Iterator<ExtensionsFile> it = installedExtensions.values().iterator();
     397    Iterator<ExtensionsFile> it = installedFiles.values().iterator();
    197398    int numLoaded = 0;
     399    int numError = 0;
    198400    while (it.hasNext())
    199401    {
    200402      ExtensionsFile extFile = it.next();
    201       // Only load new or updated file, unless forced
    202       boolean isModified = extFile.isModified();
    203       if (forceUpdate || isModified)
    204       {
    205         log.debug(extFile.getName() + " is modified or new.");
    206         // Set home directory for resources on value converters
    207         String homePath = resourcesUrl + "/" + extFile.getName();
    208         variableConverter.setVariable("HOME", homePath);
    209         pathConverter.setHome(homePath);
    210         log.debug("Home URL=" + homePath);
    211         extFile.loadExtensions();
    212         log.debug(extFile.getName() + " has been loaded");
    213         numLoaded++;
    214       }
    215       else
    216       {
    217         log.debug("Extension " + extFile.getName() + " is up to date. Skipping.");
     403      try
     404      {
     405        // Only load new or updated file, unless forced
     406        boolean isModified = extFile.isModified();
     407        boolean isNew = extFile.isNew();
     408        if (forceUpdate || isModified)
     409        {
     410          log.debug(extFile.getName() + " is " +
     411              (isNew ? "new" : isModified ? "modified" : "forced update"));
     412         
     413          // Set home directory for resources on value converters
     414          String homePath = resourcesUrl + "/" + extFile.getName();
     415          variableConverter.setVariable("HOME", homePath);
     416          pathConverter.setHome(homePath);
     417          log.debug("Home URL=" + homePath);
     418          extFile.loadExtensions();
     419          log.debug(extFile.getName() + " has been loaded");
     420          numLoaded++;
     421        }
     422        else
     423        {
     424          log.debug("Extension " + extFile.getName() + " is up to date. Skipping.");
     425        }
     426      }
     427      catch (Exception ex)
     428      {
     429        results.addErrorMessage(extFile,
     430            "Could not load extensions: " + ex.getMessage());
     431        log.error("Could not load definitions" , ex);
     432        numError++;
    218433      }
    219434    }
    220435    log.info(numLoaded + " extensions loaded from directory: " +
    221436        extensionsDir.getAbsolutePath());
    222   }
    223  
    224  
    225   private void extractResources(boolean forceUpdate)
     437    if (numError > 0)
     438    {
     439      log.warn(numError + " extensions could not be loaded from directory: " +
     440        extensionsDir.getAbsolutePath());
     441    }
     442  }
     443 
     444  /**
     445    Extract resources from JAR files. All files in /resources directory
     446    inside extension JAR files are extract to the home directory of the
     447    extension. Unless forceUpdate is TRUE only resources which has a different
     448    length or last modified time than already existing files on the
     449    disk will be extracted.
     450  */
     451  private void extractResources(boolean forceUpdate, ScanResults results)
    226452  {
    227453    // Extract resources for new and modified extensions
    228454    Pattern resourcePattern = Pattern.compile("resources/(.*)");
    229     for (ExtensionsFile extFile : installedExtensions.values())
    230     {
    231       if (forceUpdate || extFile.isModified())
    232       {
    233         File homeDir = new File(resourcesDir, extFile.getName());
    234         extFile.extractResources(homeDir, resourcePattern, "$1", forceUpdate);
    235       }
    236     }
    237   }
    238  
    239   private void registerExtensions(Registry registry)
    240   {
    241     for (ExtensionsFile extFile : installedExtensions.values())
    242     {
    243       if (extFile.isModified())
    244       {
    245         extFile.registerExtensionPoints(registry);
     455    for (ExtensionsFile extFile : installedFiles.values())
     456    {
     457      try
     458      {
     459        if (forceUpdate || extFile.isModified())
     460        {
     461          File homeDir = new File(resourcesDir, extFile.getName());
     462          int numResources = extFile.extractResources(homeDir, resourcePattern, "$1", forceUpdate);
     463          if (numResources > 0)
     464          {
     465            results.addMessage(extFile,
     466              numResources + " resources extracted successfully.");
     467          }
     468        }
     469      }
     470      catch (Exception ex)
     471      {
     472        results.addErrorMessage(extFile, "Could not extract resources: " + ex.getMessage());
     473        log.error("Could not extract resources from " + extFile.getName(), ex);
     474      }
     475    }
     476  }
     477 
     478  /**
     479    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)
     483  {
     484    for (ExtensionsFile extFile : installedFiles.values())
     485    {
     486      try
     487      {
     488        int num = extFile.registerExtensionPoints(registry, forceUpdate);
     489        if (num > 0)
     490        {
     491          results.addMessage(extFile, num + " extension points registered.");
     492        }
     493      }
     494      catch (Exception ex)
     495      {
     496        results.addErrorMessage(extFile,
     497          "Could not register extension points: " + ex.getMessage());
     498        log.error("Could not register extension points from " + extFile.getName(), ex);
    246499      }
    247500    }
    248501   
    249     for (ExtensionsFile extFile : installedExtensions.values())
    250     {
    251       extFile.registerExtensions(registry);
    252       extFile.resetModified();
     502    for (ExtensionsFile extFile : installedFiles.values())
     503    {
     504      try
     505      {
     506        int num = extFile.registerExtensions(registry, forceUpdate);
     507        extFile.resetModified();
     508        results.addMessage(extFile, num + " extensions registered.");
     509      }
     510      catch (Exception ex)
     511      {
     512        results.addErrorMessage(extFile,
     513          "Could not register extensions: " + ex.getMessage());
     514        log.error("Could not register extensions from " + extFile.getName(), ex);
     515      }
    253516    }
    254517  }
     
    262525    return loader;
    263526  }
    264    
     527 
     528  private void resetIgnore()
     529  {
     530    ignore.clear();
     531    ignore.addAll(staticIgnore);
     532  }
    265533}
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/ExtensionsFile.java

    r4187 r4198  
    2626import java.io.FileInputStream;
    2727import java.io.FileOutputStream;
     28import java.io.IOException;
    2829import java.io.InputStream;
    2930import java.io.OutputStream;
    3031import java.util.ArrayList;
     32import java.util.Collections;
     33import java.util.Iterator;
    3134import java.util.List;
    3235import java.util.regex.Matcher;
     
    3639import java.util.zip.ZipInputStream;
    3740
     41import net.sf.basedb.core.plugin.About;
    3842import net.sf.basedb.util.FileUtil;
    3943import net.sf.basedb.util.JarClassLoader;
     44import net.sf.basedb.util.extensions.Extension;
     45import net.sf.basedb.util.extensions.ExtensionPoint;
    4046import net.sf.basedb.util.extensions.Registry;
    4147import net.sf.basedb.util.extensions.xml.XmlLoader;
     
    4854  @author nicklas
    4955  @version 2.7
    50   @base.modified $Date$
     56  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    5157*/
    5258public class ExtensionsFile
     
    5561    org.apache.log4j.LogManager.getLogger("net.sf.basedb.clients.web.extensions.ExtensionsFile");
    5662
     63  private final ExtensionsDirectory directory;
    5764  private final File file;
    5865  private final XmlLoader loader;
     
    6471  private long lastModified;
    6572  private long size;
     73  private About about;
    6674 
    6775  /**
    6876    Create a new object.
     77    @param directory The directory with extensions;
    6978    @param file The XML or JAR file containting the extension definition
    7079    @param loader The XML loader to use
    7180  */
    72   public ExtensionsFile(File file, XmlLoader loader)
    73   {
     81  public ExtensionsFile(ExtensionsDirectory directory, File file, XmlLoader loader)
     82  {
     83    this.directory = directory;
    7484    this.file = file;
    7585    this.loader = loader;
     
    122132    @see #isModified()
    123133  */
    124   public void resetModified()
     134  void resetModified()
    125135  {
    126136    lastModified = file.lastModified();
    127137    size = file.length();
     138  }
     139 
     140  public boolean isNew()
     141  {
     142    return lastModified == 0;
    128143  }
    129144 
     
    148163  }
    149164 
    150   public void loadExtensions()
     165  /**
     166    Get an iterator returning all extension points that
     167    are defined by this file. The returned iterator doesn't support
     168    removal and will not reflect changes made by the same or other
     169    threads after this call has returned.
     170  */
     171  public Iterator<ExtensionPoint<?>> getExtensionPoints()
     172  {
     173    // Create a copy, or the iterator will fail if new
     174    // files are added by another thread
     175    List<ExtensionPoint<?>> copy =
     176      new ArrayList<ExtensionPoint<?>>(loader.getExtensionPoints());
     177    return Collections.unmodifiableList(copy).iterator();
     178  }
     179
     180  /**
     181    Get an iterator returning all extensions that
     182    are defined by this file. The returned iterator doesn't support
     183    removal and will not reflect changes made by the same or other
     184    threads after this call has returned.
     185  */
     186  public Iterator<Extension<?>> getExtensions()
     187  {
     188    // Create a copy, or the iterator will fail if new
     189    // files are added by another thread
     190    List<Extension<?>> copy =
     191      new ArrayList<Extension<?>>(loader.getExtensions());
     192    return Collections.unmodifiableList(copy).iterator();
     193  }
     194 
     195  /**
     196    Get information about this extensions file.
     197    @return An About object or null if no information is available
     198  */
     199  public About getAbout()
     200  {
     201    return about;
     202  }
     203 
     204  /**
     205    Get a XML-like string representation of the parameters that was
     206    used to initialise a factory. The factory must belong to an
     207    extension or extension point defined by this file.
     208   
     209    @param factory The factory object
     210    @return The parameters as found in the XML file
     211  */
     212  public String getFactoryParameters(Object factory)
     213  {
     214    return loader.getFactoryParameters(factory);
     215  }
     216 
     217  void loadExtensions()
    151218  {
    152219    if (isJar)
    153220    {
    154       loadExtensionsJar();
     221      about = loadExtensionsJar();
    155222    }
    156223    else
    157224    {
    158       loadExtensionsXml();
     225      about = loadExtensionsXml();
    159226    }
    160227  }
     
    167234  }
    168235 
    169   private void loadExtensionsXml()
    170   {
     236  private About loadExtensionsXml()
     237  {
     238    About about = null;
    171239    try
    172240    {
    173       loader.loadXmlFile(new FileInputStream(file), file.getAbsolutePath(), null);
     241      about = loader.loadXmlFile(new FileInputStream(file),
     242        file.getAbsolutePath(), null, true);
    174243    }
    175244    catch (Exception ex)
     
    177246      throw new RuntimeException(ex);
    178247    }
    179   }
    180  
    181   private void loadExtensionsJar()
     248    return about;
     249  }
     250 
     251  private About loadExtensionsJar()
    182252  {
    183253    log.info("Loading extension JAR file: " + file.getName());
     254    InputStream in = null;
     255    ZipFile zipFile = null;
     256    About about = null;
    184257    try
    185258    {
    186259      ClassLoader jarLoader = JarClassLoader.getInstance(file.getAbsolutePath(), true);
    187       ZipFile zipFile = new ZipFile(file);
     260      zipFile = new ZipFile(file);
    188261      ZipEntry zipEntry = zipFile.getEntry("META-INF/extensions.xml");
    189       InputStream in = zipFile.getInputStream(zipEntry);
    190       loader.loadXmlFile(in, file.getAbsolutePath(), jarLoader);
    191       in.close();
    192       zipFile.close();
     262      in = zipFile.getInputStream(zipEntry);
     263      about = loader.loadXmlFile(in, file.getAbsolutePath(), jarLoader, true);
    193264    }
    194265    catch (Exception ex)
     
    197268      throw new RuntimeException(ex);
    198269    }
     270    finally
     271    {
     272      try
     273      {
     274        in.close();
     275        zipFile.close();
     276      }
     277      catch (IOException ex)
     278      {}
     279    }
     280    return about;
    199281  }
    200282 
     
    213295    @return The number of extracted files
    214296  */
    215   public int extractResources(File toDir, Pattern filter, String replacement, boolean forceOverwrite)
     297  int extractResources(File toDir, Pattern filter, String replacement, boolean forceOverwrite)
    216298  {
    217299    if (!isJar) return 0;
     
    295377  }
    296378 
    297   public int removeResources(File dir)
     379  int removeResources(File dir)
    298380  {
    299381    log.info("Removing resource files from " + dir.getAbsolutePath());
     
    338420  }
    339421 
    340   public void registerExtensions(Registry registry)
    341   {
    342     loader.registerExtensions(registry);
    343   }
    344  
    345   public void registerExtensionPoints(Registry registry)
    346   {
    347     loader.registerExtensionPoints(registry);
    348   }
    349  
    350   public void unregisterAll(Registry registry)
    351   {
    352     loader.unregisterExtensionPoints(registry);
    353     loader.unregisterExtensions(registry);
     422  int registerExtensions(Registry registry, boolean forceUpdate)
     423  {
     424    for (Extension ext : loader.getExtensions())
     425    {
     426      registry.setAttribute(ext.getId(), "FILE", this);
     427    }
     428    return loader.registerExtensions(registry, forceUpdate || isModified());
     429  }
     430 
     431  int registerExtensionPoints(Registry registry, boolean forceUpdate)
     432  {
     433    for (ExtensionPoint ep : loader.getExtensionPoints())
     434    {
     435      registry.setAttribute(ep.getId(), "FILE", this);
     436    }
     437    return loader.registerExtensionPoints(registry, forceUpdate || isModified());
     438  }
     439 
     440  int unregisterAll(Registry registry)
     441  {
     442    int numExt = loader.unregisterExtensionPoints(registry);
     443    numExt += loader.unregisterExtensions(registry);
     444    return numExt;
    354445  }
    355446 
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/Settings.java

    r4187 r4198  
    4242
    4343/**
    44   Keeps settings for the extensions system. Settings
     44  Class for keeping settings for the extensions system. Settings
    4545  are kept in the file <code>WEB-INF/extensions/settings.xml</code>.
    4646  <p>
     
    4848  Extension points and extensions can be disabled/enabled by
    4949  {@link #enableExtensionPoint(String, boolean)} and
    50   {@link #enableExtension(String, boolean)}. 
     50  {@link #enableExtension(String, boolean)}.
    5151  <p>
     52
    5253  This class also implements the {@link ExtensionsFilter}
    5354  interface, and can be used on the
    5455  {@link Registry#useExtensions(net.sf.basedb.util.extensions.Context, ExtensionsFilter, String...)}
    5556  method. This means that disabling/enabling extensions and extension points
    56   will immediately be visible in the web interface.
     57  will immediately be visible in the web interface. Sorting is delegated
     58  to the {@link DefaultFilter}.
    5759
    5860  @author nicklas
    5961  @version 2.7
    60   @base.modified $Date$
     62  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    6163*/
    6264public class Settings
     
    6668 
    6769  private static final String AUTO_INSTALL = "auto-install";
    68  
    6970
    7071  private final File file;
     
    7576  private Preset disabledExtensionPoints;
    7677 
     78  /**
     79    Create a new Settings object. If the given file doesn't exists
     80    it will be created if {@link #save()} is called. The settings file
     81    must be an XML file that can be read by the {@link Presets} class.
     82    @param file The file to load/save settings in
     83  */
    7784  public Settings(File file)
    7885  {
     
    139146  }
    140147 
     148  /**
     149    Get the interval, in seconds, between automatic checks
     150    for new/modified/deleted extensions.
     151    @return The interval in seconds or 0 if the auto-install feature
     152     is disabled (the default)
     153  */
    141154  public int getAutoInstall()
    142155  {
    143     return Values.getInt(settings.getSetting(AUTO_INSTALL));
     156    int autoInstall = Values.getInt(settings.getSetting(AUTO_INSTALL));
     157    return autoInstall > 0 ? autoInstall : 0;
    144158  }
    145159 
     160  /**
     161    Change the interval, in seconds, between automatic checks
     162    for new/modified/deleted extensions.
     163    @param autoInstall The interval in seconds, or 0 to disable the
     164      feature
     165  */
    146166  public void setAutoInstall(int autoInstall)
    147167  {
     168    if (autoInstall < 0) autoInstall = 0;
    148169    settings.setSetting(AUTO_INSTALL, Integer.toString(autoInstall));
     170    hasChanged = true;
    149171  }
    150172 
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/menu/FixedMenuItemFactory.java

    r4187 r4198  
    3939  visible and enabled. Use the setter method to change
    4040  the properties. Changes to the properties are immediately
    41   visible in the menu items returned from the {@link #getActions(Context)}
    42   method.
     41  visible in the menu items returned from the
     42  {@link #getActions(Context, Extension)} method.
    4343 
    4444  @author nicklas
    4545  @version 2.7
    46   @base.modified $Date$
     46  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    4747*/
    4848public class FixedMenuItemFactory
     
    201201  // ------------------------------------
    202202 
    203   @Override
    204   public String toString()
    205   {
    206     StringBuilder sb = new StringBuilder();
    207     sb.append(this.getClass().getName());
    208     if (type != null) sb.append("; type=" + type.name());
    209     if (style != null) sb.append("; style=" + style);
    210     if (title != null) sb.append("; title=" + title);
    211     if (onClick != null) sb.append("; onClick=" + onClick);
    212     if (icon != null) sb.append("; icon=" + icon);
    213     if (tooltip != null) sb.append("; tooltip=" + tooltip);
    214     sb.append("; visible=" + visible);
    215     sb.append("; enabled=" + enabled);
    216     sb.append(super.toString());
    217     return sb.toString();
    218   }
    219 
    220203}
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/menu/PermissionMenuItemFactory.java

    r4187 r4198  
    3535
    3636/**
    37   A menu item factory that can disable or hide
    38   menu items based on the logged in user's permissions.
     37  A menu item factory that can hide or disable menu items based
     38  on the logged in user's permissions. It is possible to set
     39  different text, tooltip, icon, etc. for a disabled menu.
    3940  <p>
    4041  The permissions are checked for a given item type
     
    4950 
    5051  If the menu item is hidden this is indicated by returning
    51   false from the {@link #prepareContext(Context)} method. Most
     52  false from the {@link #prepareContext(Context, Extension)} method. Most
    5253  properties can have different values for the enabled/disabled state.
    5354  For example:
     
    6061  @author nicklas
    6162  @version 2.7
    62   @base.modified $Date$
     63  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    6364*/
    6465public class PermissionMenuItemFactory
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/toolbar/ButtonAction.java

    r4187 r4198  
    11/**
    2   $Id$
     2  $Id:ButtonAction.java 4187 2008-03-20 11:15:25Z nicklas $
    33
    44  Copyright (C) Authors contributing to this file.
     
    3131  @author nicklas
    3232  @version 2.7
    33   @base.modified $Date$
     33  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    3434 */
    3535public interface ButtonAction
     
    7575    It is recommended that the image is 16x16 pixels to
    7676    line up with the other icons used by the BASE.
    77     The reference will be path converted as described by
    78     {@link ExtensionsDirectory#makeAbsolutePath(String, net.sf.basedb.util.extensions.Extension)}
    7977   
    8078    @return A reference to an image, or null if no image should
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/toolbar/CompactButtonRenderer.java

    r4187 r4198  
    11/**
    2   $Id$
     2  $Id:CompactButtonRenderer.java 4187 2008-03-20 11:15:25Z nicklas $
    33
    44  Copyright (C) Authors contributing to this file.
     
    3636  @author nicklas
    3737  @version 2.7
    38   @base.modified $Date$
     38  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    3939 */
    4040public class CompactButtonRenderer
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/toolbar/CompactButtonRendererFactory.java

    r4187 r4198  
    11/**
    2   $Id$
     2  $Id:CompactButtonRendererFactory.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.toolbar;
    2525
     26import net.sf.basedb.clients.web.extensions.AbstractJspRendererFactory;
    2627import net.sf.basedb.clients.web.extensions.JspContext;
    2728import net.sf.basedb.util.extensions.Context;
    2829import net.sf.basedb.util.extensions.Extension;
    29 import net.sf.basedb.util.extensions.RendererFactory;
    3030
    3131/**
     
    3333  @author nicklas
    3434  @version 2.7
    35   @base.modified $Date$
     35  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    3636 */
    3737public class CompactButtonRendererFactory
    38   implements RendererFactory<ButtonAction>
     38  extends AbstractJspRendererFactory<ButtonAction>
    3939{
    4040
    41   public void prepareContext(Context context, Extension extension)
    42   {
    43    
    44   }
     41  public CompactButtonRendererFactory()
     42  {}
    4543 
    4644  public CompactButtonRenderer getRenderer(Context context, Extension extension)
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/toolbar/FixedButtonFactory.java

    r4187 r4198  
    3838  The button is by default both visible and enabled. Use the setter
    3939  method to change the properties. Changes to the properties are immediately
    40   visible in the menu items returned from the {@link #getActions(Context)}
     40  visible in the menu items returned from the {@link #getActions(Context, Extension)}
    4141  method.
    4242  <p>
     
    4949  @author nicklas
    5050  @version 2.7
    51   @base.modified $Date$
     51  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    5252*/
    5353public class FixedButtonFactory
     
    207207  }
    208208
    209   @Override
    210   public String toString()
    211   {
    212     StringBuilder sb = new StringBuilder();
    213     sb.append(this.getClass().getName());
    214     if (id != null) sb.append("; id=" + id);
    215     if (clazz != null) sb.append("; clazz=" + clazz);
    216     if (style != null) sb.append("; style=" + style);
    217     if (title != null) sb.append("; title=" + title);
    218     if (onClick != null) sb.append("; onClick=" + onClick);
    219     if (icon != null) sb.append("; icon=" + icon);
    220     if (tooltip != null) sb.append("; tooltip=" + tooltip);
    221     sb.append("; visible=" + visible);
    222     sb.append("; enabled=" + enabled);
    223     if (getScripts() != null) sb.append("; scripts=" + getScripts());
    224     if (getStylesheets() != null) sb.append("; stylesheets=" + getStylesheets());
    225     return sb.toString();
    226   }
    227 
    228209}
  • trunk/src/clients/web/net/sf/basedb/clients/web/servlet/ExtensionsServlet.java

    r4187 r4198  
    3232import net.sf.basedb.clients.web.extensions.ExtensionsControl;
    3333import net.sf.basedb.clients.web.extensions.ExtensionsDirectory;
    34 import net.sf.basedb.clients.web.extensions.Settings;
    35 import net.sf.basedb.util.extensions.Registry;
    3634
    3735/**
    38   Servlet for starting the extension system. The servlet should be
    39   configured with <code>&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;</code>
    40   in the web.xml file. When the servlet is loaded it will initialise the
    41   {@link ExtensionsControl} class.
     36  Servlet for starting the extension system when the web server starts.
     37  The servlet should be configured with the
     38  <code>&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;</code>
     39  option in the web.xml file. When the servlet is loaded it will initialise the
     40  {@link ExtensionsControl} class. This servlet is not used to handle
     41  any requests.
    4242 
    4343  @author nicklas
     
    6464    String rootUrl = context.getContextPath();
    6565   
    66     Settings settings = new Settings(new File(extensionsDir, "settings.xml"));
    6766    ExtensionsDirectory extensions = new ExtensionsDirectory(
    6867        extensionsDir, resourcesDir, rootUrl, rootUrl + ExtensionsControl.RESOURCES_URL);
    69     ExtensionsControl.init(new Registry(), extensions, settings);
     68    ExtensionsControl.init(extensions);
    7069  }
    7170
  • trunk/src/core/net/sf/basedb/core/xsd/extensions.xsd

    r4187 r4198  
    3232    <xsd:complexType>
    3333      <xsd:sequence>
     34        <xsd:element name="about" type="aboutType" minOccurs="0" />
    3435        <xsd:element name="extension-point" minOccurs="0" maxOccurs="unbounded">
    3536          <xsd:complexType>
  • trunk/src/core/net/sf/basedb/util/extensions/AboutBean.java

    r4187 r4198  
    3232  @author nicklas
    3333  @version 2.7
    34   @base.modified $Date$
     34  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    3535*/
    3636public class AboutBean
     
    121121  }
    122122
     123  /**
     124    Copy information from antoher <code>About</code> object
     125    to this one.
     126    @param from The object to copy from
     127    @param overwrite TRUE copy all values, FALSE
     128      to only copy if the existing value is null
     129  */
     130  public void copy(About from, boolean overwrite)
     131  {
     132    if (from == null) return;
     133    name = overwrite || name == null ? from.getName() : name;
     134    description = overwrite || description == null ? from.getDescription() : description;
     135    version = overwrite || version == null ? from.getVersion() : version;
     136    copyright = overwrite || copyright == null ? from.getCopyright() : copyright;
     137    contact = overwrite || contact == null ? from.getContact() : contact;
     138    email = overwrite || email == null ? from.getEmail() : email;
     139    url = overwrite || url == null ? from.getUrl() : url;
     140  }
     141
    123142}
  • trunk/src/core/net/sf/basedb/util/extensions/Action.java

    r4187 r4198  
    11/**
    2   $Id$
     2  $Id:Action.java 4187 2008-03-20 11:15:25Z nicklas $
    33
    44  Copyright (C) Authors contributing to this file.
     
    3030  interface that needs to be implemented by all action objects.
    3131  <p>
    32   The extension point defines the actual subclass of actions that
     32  This interface is usually subclassed for specific types of actions.
     33  It is the extension point that decides which action class that
    3334  can be used in that particular extension point. See
    3435  {@link ExtensionPoint#getActionClass()}.
    3536  <p>
    3637  The core doesn't define any actual action implementations, since they
    37   are completely dependent on the implementing client. For examples,
     38  always depends on the client application. For examples,
    3839  see the web client extensions {@link net.sf.basedb.clients.web.extensions}.
    3940
    4041  @author nicklas
    4142  @version 2.7
    42   @base.modified $Date$
     43  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    4344*/
    4445public interface Action
  • trunk/src/core/net/sf/basedb/util/extensions/ActionFactory.java

    r4187 r4198  
    11/**
    2   $Id$
     2  $Id:ActionFactory.java 4187 2008-03-20 11:15:25Z nicklas $
    33
    44  Copyright (C) Authors contributing to this file.
     
    4848  @author nicklas
    4949  @version 2.7
    50   @base.modified $Date$
     50  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    5151*/
    5252public interface ActionFactory<A extends Action>
     
    6666      With the BASE web-client this means that it is possible to
    6767      add scripts or stylesheets that is needed by the extension.
    68       See {@link JspContext}.
     68      See {@link net.sf.basedb.clients.web.extensions.JspContext}.
    6969    </ul>
    7070   
  • trunk/src/core/net/sf/basedb/util/extensions/ActionIterator.java

    r4187 r4198  
    5555  @author nicklas
    5656  @version 2.7
    57   @base.modified $Date$
     57  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    5858*/
    5959public class ActionIterator<A extends Action>
     
    9494    this.rendererCache = new IdentityHashMap<Object, Renderer<? super A>>();
    9595  }
    96  
     96  /*
     97    From the Iterator interface
     98    ---------------------------
     99  */
    97100  public boolean hasNext()
    98101  {
     
    140143    return actions[offset];
    141144  }
    142  
    143145  /**
    144146    Not supported.
     
    149151    throw new UnsupportedOperationException();
    150152  }
     153  // ------------------------------------
    151154 
    152155  /**
  • trunk/src/core/net/sf/basedb/util/extensions/Context.java

    r4187 r4198  
    11/**
    2   $Id$
     2  $Id:Context.java 4187 2008-03-20 11:15:25Z nicklas $
    33
    44  Copyright (C) Authors contributing to this file.
     
    6363  @author nicklas
    6464  @version 2.7
    65   @base.modified $Date$
     65  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    6666*/
    6767public class Context
     
    107107    object, but the extension point may declare (in it's
    108108    documentation) that the current item is always of a
    109     certain type.
     109    certain class.
    110110    @return The current item, or null
    111111  */
  • trunk/src/core/net/sf/basedb/util/extensions/DefaultFilter.java

    r4187 r4198  
    3636  @author nicklas
    3737  @version 2.7
    38   @base.modified $Date$
     38  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    3939*/
    4040public class DefaultFilter
     
    4545    Comparator used to order extensions by their index value.
    4646    @see Extension#getIndex()
    47    */
     47  */
    4848  public static final Comparator<Extension<?>> INDEX_COMPARATOR =
    4949    new Comparator<Extension<?>>()
  • trunk/src/core/net/sf/basedb/util/extensions/Registry.java

    r4187 r4198  
    11/**
    2   $Id$
     2  $Id:Registry.java 4187 2008-03-20 11:15:25Z nicklas $
    33
    44  Copyright (C) Authors contributing to this file.
     
    5050  @author nicklas
    5151  @version 2.7
    52   @base.modified $Date$
     52  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    5353  @see net.sf.basedb.clients.web.extensions.ExtensionsDirectory
    5454*/
     
    5858  private final Map<String, RegisteredExtensionPoint<?>> extensionPoints;
    5959  private final Map<String, RegisteredExtension<?>> extensions;
    60  
     60  private Map<AttributeKey, Object> attributes;
     61
    6162  /**
    6263    Create a new empty registry.
     
    6667    extensionPoints = new HashMap<String, RegisteredExtensionPoint<?>>();
    6768    extensions = new HashMap<String, RegisteredExtension<?>>();
     69  }
     70 
     71  public Object getAttribute(Object registered, String name)
     72  {
     73    if (registered == null || name == null || attributes == null) return null;
     74    AttributeKey key = new AttributeKey(registered, name);
     75    return attributes.get(key);
     76  }
     77 
     78  public void setAttribute(Object registered, String name, Object value)
     79  {
     80    if (registered == null || name == null) return;
     81    AttributeKey key = new AttributeKey(registered, name);
     82    if (attributes == null) attributes = new HashMap<AttributeKey, Object>();
     83    if (value == null)
     84    {
     85      attributes.remove(key);
     86    }
     87    else
     88    {
     89      attributes.put(key, value);
     90    }
    6891  }
    6992 
     
    94117  */
    95118  @SuppressWarnings("unchecked")
    96   public synchronized <A extends Action> void registerExtensionPoint(ExtensionPoint<A> extensionPoint)
     119  public synchronized <A extends Action> void
     120    registerExtensionPoint(ExtensionPoint<A> extensionPoint)
    97121  {
    98122    if (extensionPoint == null) throw new InvalidUseOfNullException("extensionPoint");
    99123
    100     if (extensionPoint.getId() == null)
    101     {
    102       throw new InvalidUseOfNullException("extensionPoint.id");
     124    String id = extensionPoint.getId();
     125    if (id == null)
     126    {
     127      throw new InvalidUseOfNullException("extensionPoint.id of " + extensionPoint);
    103128    }
    104129    Class<A> ac = extensionPoint.getActionClass();
    105130    if (ac == null)
    106131    {
    107       throw new InvalidUseOfNullException("extensionPoint.actionClass");
     132      throw new InvalidUseOfNullException("Action class of extensionPoint[id=" + id + "]");
    108133    }
    109134    if (!Action.class.isAssignableFrom(ac))
    110135    {
    111       throw new ClassCastException("Class " + ac.getName() + " doesn't implement the 'Action' class");
     136      throw new ClassCastException("Action class '" + ac.getName() +
     137        "' of extensionPoint[id=" + id + "] doesn't implement the 'Action' class");
    112138    }
    113139   
     
    199225      throw new InvalidUseOfNullException("extension");
    200226    }
    201     if (extension.getId() == null)
    202     {
    203       throw new InvalidUseOfNullException("extension.id");
     227    String id = extension.getId();
     228    if (id == null)
     229    {
     230      throw new InvalidUseOfNullException("extension.id of " + extension);
    204231    }
    205232    if (extension.getActionFactory() == null)
    206233    {
    207       throw new InvalidUseOfNullException("extension.actionFactory");
     234      throw new InvalidUseOfNullException("Action factory of extension[id=" + id + "]");
    208235    }
    209236
     
    332359    called, it is only called once with a null argument for the extension.
    333360    Renderer factories that are attached to extensions are called once for each
    334     extension, but only if the extension point allows it.
     361    enabled extension, but only if the extension point allows renderer overriding.
    335362    See {@link ExtensionPoint#allowRendererOverride()}.
    336363   
     
    345372      If this value is null, no extensions are filtered out and they
    346373      are sorted according to their {@link Extension#getIndex()} value
    347     @param extensionPoints An array of extension point ID:s to get
     374    @param extensionPointIds An array of extension point ID:s to get
    348375      extension for. In most cases the extension points should
    349376      require the same action class but this is not required
     
    353380  @SuppressWarnings("unchecked")
    354381  public ExtensionsInvoker<?> useExtensions(Context context, ExtensionsFilter filter,
    355     String... extensionPoints)
     382    String... extensionPointIds)
    356383  {
    357384    if (filter == null) filter = DEFAULT_FILTER;
    358385    List<RegisteredExtension<Action>> use = new LinkedList<RegisteredExtension<Action>>();
    359     for (String id : extensionPoints)
    360     {
     386    for (String id : extensionPointIds)
     387    {
     388      // Find a registered extension point for each ID
    361389      RegisteredExtensionPoint<Action> rep =
    362         (RegisteredExtensionPoint<Action>)this.extensionPoints.get(id);
     390        (RegisteredExtensionPoint<Action>)extensionPoints.get(id);
    363391      if (rep != null && filter.isEnabled(rep))
    364392      {
     393        // The extension point is enabled...
    365394        boolean allowRenderOverride = rep.allowRendererOverride();
    366395        int enabledExtensions = 0;
     396        // ... find the extensions for it
    367397        for (RegisteredExtension<Action> ext : rep.getExtensions())
    368398        {
     399          // Check with the filter if the extension is enabled...
     400          // ... and check with the action factory as well
    369401          if (filter.isEnabled(ext) && ext.getActionFactory().prepareContext(context, ext))
    370402          {
    371403            use.add(ext);
    372404            enabledExtensions++;
     405            // Prepare the renderer factory if it is allowed and there is one
    373406            if (allowRenderOverride)
    374407            {
     
    378411          }
    379412        }
     413        // Prepare the renderer factory if there is at least one extension
    380414        RendererFactory<? super Action> factory = rep.getRendererFactory();
    381415        if (enabledExtensions > 0 && factory != null)
     
    385419      }
    386420    }
     421    // Sort the extensions
    387422    filter.sort(use);
    388423    return new ExtensionsInvoker<Action>(use, context);
     
    404439    private final String id;
    405440    private final Class<A> actionClass;
     441    private final Map<String, RegisteredExtension<A>> extensions;
     442
    406443    private String name;
    407444    private String description;
     
    409446    private boolean allowRendererOverride;
    410447   
    411     private Map<String, RegisteredExtension<A>> extensions;
    412    
    413448    /**
    414449      Create a new registered extension point by copying the
     
    418453    {
    419454      this.id = ep.getId();
    420       this.name = ep.getName();
    421       this.description = ep.getDescription();
    422455      this.actionClass = ep.getActionClass();
    423       this.rendererFactory = ep.getRendererFactory();
    424       this.allowRendererOverride = ep.allowRendererOverride();
    425456      this.extensions = new HashMap<String, RegisteredExtension<A>>();
     457      update(ep);
    426458    }
    427459   
     
    701733  }
    702734 
     735  static class AttributeKey
     736  {
     737    private final Object o;
     738    private final String name;
     739    AttributeKey(Object o, String name)
     740    {
     741      this.o = o;
     742      this.name = name;
     743    }
     744   
     745    @Override
     746    public int hashCode()
     747    {
     748      return 13 * o.hashCode() + 5 * name.hashCode();
     749    }
     750   
     751    @Override
     752    public boolean equals(Object other)
     753    {
     754      if (this == other) return true;
     755      if (other == null || other.getClass() != this.getClass()) return false;
     756      AttributeKey ak = (AttributeKey)other;
     757      return this.o.equals(ak.o) && this.name.equals(ak.name);
     758    }
     759   
     760  }
     761 
    703762}
  • trunk/src/core/net/sf/basedb/util/extensions/RendererFactory.java

    r4187 r4198  
    11/**
    2   $Id$
     2  $Id:RendererFactory.java 4187 2008-03-20 11:15:25Z nicklas $
    33
    44  Copyright (C) Authors contributing to this file.
     
    5151  @author nicklas
    5252  @version 2.7
    53   @base.modified $Date$
     53  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    5454*/
    5555public interface RendererFactory<A extends Action>
     
    6161    with resources that the actions may need. With the BASE web-client
    6262    this means that it is possible to add scripts or stylesheets that
    63     is needed by the extension. See {@link JspContext}.
     63    is needed by the extension. See
     64    {@link net.sf.basedb.clients.web.extensions.JspContext}.
    6465   
    6566    <p>
    66     Note! This method has no return as opposed to
    67     {@link ActionFactory#prepareContext(Context, Extension). The
     67    Note! This method has no return value as opposed to
     68    {@link ActionFactory#prepareContext(Context, Extension)}. The
    6869    simple reason is that once we get to the point of rendering it
    6970    is already known that the extension is enabled.
  • trunk/src/core/net/sf/basedb/util/extensions/xml/PathConverter.java

    r4187 r4198  
    3232 
    3333  <ul>
    34   <li>If the value starts with '/', the BASE root path is added to the start
    35     of the value, for example, <code>/images/copy.gif --&gt; /base/images.copy.gif</code>.
     34  <li>If the value starts with '/', the root path is added to the start
     35    of the value, for example, <code>/images/copy.gif --&gt; /base2/images/copy.gif</code>.
    3636  <li>If the value starts with '~', the path to the home directory for
    3737    the current extension is added, for example,
    38     <code>~/images/myimage.png --&gt; /base/extensions/xxxx/images/myimage.png</code>
     38    <code>~/images/myimage.png --&gt; /base2/extensions/my-extensions.jar/images/myimage.png</code>
    3939  </ul>
    4040
     
    5050  @author nicklas
    5151  @version 2.7
    52   @base.modified $Date$
     52  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    5353*/
    5454public class PathConverter
  • trunk/src/core/net/sf/basedb/util/extensions/xml/ValueConverter.java

    r4187 r4198  
    3535  with the {@link XmlLoader#addValueConverter(ValueConverter)} method.
    3636  Before a factory setter method is called, all registered
    37   converter will get the chance to {@link #convert(String, Method)}
     37  converters will get the chance to {@link #convert(String, Method)}
    3838  the parameter value from the XML file. The converters are
    3939  called in the order they are registered.
     
    6363  @author nicklas
    6464  @version 2.7
    65   @base.modified $Date$
     65  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    6666*/
    6767public interface ValueConverter
  • trunk/src/core/net/sf/basedb/util/extensions/xml/VariableConverter.java

    r4187 r4198  
    3838  annotation. The input string will be scanned for
    3939  parts that match <code>$VARIABLE$</code> and have that part
    40   replaced by the actual value of a defined variable.
    41   See {@link #setVariable(String, String)} for more details.
     40  replaced by the actual value of a defined variable. Undefined
     41  variables are not replaced. See {@link #setVariable(String, String)}
     42  for more details.
    4243
    4344  <p>
    4445  Web client note! The web client application defines the variables
    4546  <code>ROOT</code> and <code>HOME</code> which are the paths to
    46   the web application root directory (<code>/base</code>) and the
    47   home directory (<code>/base/extensions/xxxx</code>) of the current
     47  the web application root directory (<code>/base2</code>) and the
     48  home directory (<code>/base2/extensions/xxxx</code>) of the current
    4849  extension. This means that the XML configuration file may
    4950  contain values like <code>$ROOT$/images/copy.gif</code>. The
     
    5455  @author nicklas
    5556  @version 2.7
    56   @base.modified $Date$
     57  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    5758*/
    5859public class VariableConverter
     
    9899  /**
    99100    Set the value of a variable. If a setter method of a factory class
    100     ({@link ActionFactory} and {@link RendererFactory}) is annotated with the
    101     {@link VariableSetter} and/or {@link PathSetter} annotation the
     101    ({@link ActionFactory} or {@link RendererFactory}) is annotated with the
     102    {@link VariableSetter} annotation the
    102103    XML loader will scan the value from the XML file for replacement
    103104    tags, <code>$VARIABLE$</code>, and replace the tag with the value
  • trunk/src/core/net/sf/basedb/util/extensions/xml/XmlLoader.java

    r4187 r4198  
    11/**
    2   $Id$
     2  $Id:XmlLoader.java 4187 2008-03-20 11:15:25Z nicklas $
    33
    44  Copyright (C) Authors contributing to this file.
     
    2828import java.lang.reflect.InvocationTargetException;
    2929import java.lang.reflect.Method;
    30 import java.util.ArrayList;
     30import java.util.HashMap;
     31import java.util.LinkedHashSet;
    3132import java.util.LinkedList;
    3233import java.util.List;
     34import java.util.Map;
     35import java.util.Set;
    3336
    3437import org.jdom.Document;
     
    3639import org.jdom.JDOMException;
    3740import org.jdom.Namespace;
    38 
     41import org.jdom.output.Format;
     42import org.jdom.output.XMLOutputter;
     43
     44import net.sf.basedb.core.InvalidUseOfNullException;
     45import net.sf.basedb.core.plugin.About;
    3946import net.sf.basedb.util.ClassUtil;
    4047import net.sf.basedb.util.Values;
     
    98105  @author nicklas
    99106  @version 2.7
    100   @base.modified $Date$
     107  @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
    101108*/
    102109public class XmlLoader
     
    114121  private static final String namespace = "http://base.thep.lu.se/extensions.xsd";
    115122 
     123  // To convert XML elements to a string representation
     124  private XMLOutputter xmlOut;
     125 
    116126  // All loaded extension points
    117127  private List<ExtensionPoint<Action>> extensionPoints;
     
    121131 
    122132  // List of registered converters
    123   private List<ValueConverter> converters;
     133  private Set<ValueConverter> converters;
     134 
     135  // Map with XML representation of factory parameters
     136  private Map<Object, String> factoryParameters;
    124137 
    125138  /**
     
    130143    extensionPoints = new LinkedList<ExtensionPoint<Action>>();
    131144    extensions = new LinkedList<Extension<Action>>();
    132   }
    133  
    134  
     145    factoryParameters = new HashMap<Object, String>();
     146    xmlOut = new XMLOutputter(Format.getPrettyFormat());
     147  }
     148 
     149  /**
     150    Add a value converter to this loader
     151    @param converter The converter to add
     152    @throws NullPointerException If the converter is null
     153  */
    135154  public void addValueConverter(ValueConverter converter)
    136155  {
    137     if (converters == null) converters = new ArrayList<ValueConverter>();
     156    if (converter == null) throw new InvalidUseOfNullException("converter");
     157    if (converters == null) converters = new LinkedHashSet<ValueConverter>();
    138158    converters.add(converter);
     159  }
     160
     161  /**
     162    Remove a converter.
     163    @param converter The converter to remove, null values are ignored
     164  */
     165  public void removeValueConverter(ValueConverter converter)
     166  {
     167    if (converter == null || converters == null) return;
     168    converters.remove(converter);
     169  }
     170 
     171  /**
     172    Remove all value converters.
     173  */
     174  public void clearValueConverters()
     175  {
     176    if (converters != null) converters.clear();
    139177  }
    140178 
     
    149187      named in the XML file, or null to use the default classloader (=the same
    150188      class loader that loaded the BASE core classes)
     189    @param clear TRUE to clear all already loaded extensions before loading
     190      the extensions in this file
     191    @return Information about the extension, or null if no such information
     192      is available
    151193    @throws JDOMException If validation of the XML file fails
    152194    @throws IOException If there is an error reading the XML file
     
    160202      for example, because it is an abstract class or an interface
    161203  */
    162   public void loadXmlFile(InputStream xmlFile, String filename,
    163       ClassLoader classLoader)
     204  public About loadXmlFile(InputStream xmlFile, String filename,
     205      ClassLoader classLoader, boolean clear)
    164206    throws JDOMException, IOException, ClassNotFoundException, NoSuchMethodException,
    165207      IllegalAccessException, InstantiationException
    166208  {
    167     Document dom = XMLUtil.getSchemaValidatedXML(xmlFile, filename, namespace, schemaFileURL);
    168     loadExtensionPoints(dom, classLoader);
    169     loadExtensions(dom, classLoader);
     209    if (clear)
     210    {
     211      extensionPoints.clear();
     212      extensions.clear();
     213    }
     214    Document dom = loadDocument(xmlFile, filename);
     215    About globalAbout = loadGlobalAbout(dom);
     216    loadExtensionPoints(dom, classLoader, globalAbout);
     217    loadExtensions(dom, classLoader, globalAbout);
     218    return globalAbout;
    170219  }
    171220 
     
    184233  {
    185234    return extensions;
     235  }
     236 
     237  /**
     238    Get a XML-like string representation of the parameters that was
     239    used to initialise a factory.
     240    @param factory The factory object
     241    @return The parameters as found in the XML file
     242  */
     243  public String getFactoryParameters(Object factory)
     244  {
     245    return factoryParameters.get(factory);
    186246  }
    187247 
     
    189249    Register all loaded extension points with a registry.
    190250    @param registry The registry
    191   */
    192   public void registerExtensionPoints(Registry registry)
    193   {
     251    @param update TRUE to update already registered extension
     252      points, FALSE to ignore
     253    @return The number of extension points registered or updated
     254    @see Registry#registerExtensionPoint(ExtensionPoint)
     255  */
     256  public int registerExtensionPoints(Registry registry, boolean update)
     257  {
     258    int numRegistered = 0;
    194259    for (ExtensionPoint<Action> ep : extensionPoints)
    195260    {
    196       registry.registerExtensionPoint(ep);
    197     }
     261      if (update || !registry.extensionPointIsRegistered(ep.getId()))
     262      {
     263        registry.registerExtensionPoint(ep);
     264        numRegistered++;
     265      }
     266    }
     267    return numRegistered;
    198268  }
    199269 
    200270  /**
    201271    Unregister all loaded extension points from a registry.
     272    All extensions will also be unregistered.
    202273    @param registry The registry
    203   */
    204   public void unregisterExtensionPoints(Registry registry)
     274    @return The number of extension points that was unregistered
     275    @see Registry#unregisterExtensionPoint(String)
     276  */
     277  public int unregisterExtensionPoints(Registry registry)
    205278  {
    206279    for (ExtensionPoint<Action> ep : extensionPoints)
     
    208281      registry.unregisterExtensionPoint(ep.getId());
    209282    }
     283    return extensionPoints.size();
    210284  }
    211285 
     
    213287    Register all loaded extensions with a registry.
    214288    @param registry The registry
    215   */
    216   public void registerExtensions(Registry registry)
    217   {
     289    @param update TRUE to update already registered extensions
     290      FALSE to ignore
     291    @return The number of registered or updated extensions
     292    @see Registry#registerExtension(Extension)
     293  */
     294  public int registerExtensions(Registry registry, boolean update)
     295  {
     296    int numRegistered = 0;
    218297    for (Extension<Action> ext : extensions)
    219298    {
    220       registry.registerExtension(ext);
    221     }
     299      if (update || !registry.extensionIsRegistered(ext.getId()))
     300      {
     301        registry.registerExtension(ext);
     302        numRegistered++;
     303      }
     304    }
     305    return numRegistered;
    222306  }
    223307 
     
    225309    Unregister all loaded extensions from a registry.
    226310    @param registry The registry
    227   */
    228   public void unregisterExtensions(Registry registry)
     311    @return The number of extensions that was unregistered
     312    @see Registry#unregisterExtension(String)
     313  */
     314  public int unregisterExtensions(Registry registry)
    229315  {
    230316    for (Extension<Action> ext : extensions)
     
    232318      registry.unregisterExtension(ext.getId());
    233319    }
    234   }
     320    return extensions.size();
     321  }
     322 
     323  /**
     324    Load and validate the XML file.
     325    @return A JDOM document object
     326  */
     327  protected Document loadDocument(InputStream xmlFile, String filename)
     328    throws IOException, JDOMException
     329  {
     330    return XMLUtil.getSchemaValidatedXML(xmlFile, filename, namespace, schemaFileURL);
     331  }
     332 
     333  /**
     334    Load the global about tag from the document.
     335    @return An about bean or null if the about tag is not found.
     336  */
     337  protected AboutBean loadGlobalAbout(Document dom)
     338  {
     339    Element root = dom.getRootElement();
     340    Namespace ns = root.getNamespace();
     341    return loadAbout(root.getChild("about", ns));
     342  }
     343 
     344  /**
     345    Load information in an <code>&lt;about&gt;</code> tag.
     346    @param aboutTag The about tag
     347  */
     348  @SuppressWarnings("unchecked")
     349  protected AboutBean loadAbout(Element aboutTag)
     350  {
     351    AboutBean about = null;
     352    if (aboutTag != null)
     353    {
     354      Namespace ns = aboutTag.getNamespace();
     355      about = new AboutBean();
     356      about.setName(Values.getStringOrNull(aboutTag.getChildText("name", ns)));
     357      about.setDescription(Values.getStringOrNull(aboutTag.getChildText("description", ns)));
     358      about.setVersion(Values.getStringOrNull(aboutTag.getChildText("version", ns)));
     359      about.setContact(Values.getStringOrNull(aboutTag.getChildText("contact", ns)));
     360      about.setCopyright(Values.getStringOrNull(aboutTag.getChildText("copyright", ns)));
     361      about.setEmail(Values.getStringOrNull(aboutTag.getChildText("email", ns)));
     362      about.setUrl(Values.getStringOrNull(aboutTag.getChildText("url", ns)));
     363    }
     364    return about;
     365  }
     366 
    235367 
    236368  /**
     
    238370  */
    239371  @SuppressWarnings("unchecked")
    240   private void loadExtensionPoints(Document dom, ClassLoader classLoader)
     372  protected int loadExtensionPoints(Document dom, ClassLoader classLoader, About globalAbout)
    241373    throws ClassNotFoundException, NoSuchMethodException,
    242374      IllegalAccessException, InstantiationException
    243375  {
    244  
     376    int numLoaded = 0;
     377    List<ExtensionPoint<Action>> temp = new LinkedList<ExtensionPoint<Action>>();
    245378    /*
    246379      Format of an extension point is:
     
    265398    {
    266399      ExtensionPointBean<Action> extensionPoint = new ExtensionPointBean<Action>();
    267       extensionPoints.add(extensionPoint);
     400      temp.add(extensionPoint);
     401      numLoaded++;
    268402 
    269403      // Set ID (required)
     
    291425      }
    292426    }
     427    extensionPoints.addAll(temp);
     428    return numLoaded;
    293429  }
    294430 
     
    298434  */
    299435  @SuppressWarnings("unchecked")
    300   private void loadExtensions(Document dom, ClassLoader classLoader)
     436  protected int loadExtensions(Document dom, ClassLoader classLoader, About globalAbout)
    301437    throws ClassNotFoundException, NoSuchMethodException,
    302438      IllegalAccessException, InstantiationException
    303439  {
     440    int numLoaded = 0;
     441    List<Extension<Action>> temp = new LinkedList<Extension<Action>>();
    304442    /*
    305443      Format of an extension is:
     
    339477    {
    340478      ExtensionBean<Action> ext = new ExtensionBean<Action>();
    341       extensions.add(ext);
    342  
     479      temp.add(ext);
     480      numLoaded++;
     481     
    343482      // Set ID (required)
    344483      String id = Values.getStringOrNull(epTag.getAttributeValue("id"));
     
    353492     
    354493      // Set About (optional)
    355       Element aboutTag = epTag.getChild("about", ns);
    356       if (aboutTag != null)
    357       {
    358         AboutBean about = new AboutBean();
    359         ext.setAbout(about);
    360         about.setName(Values.getStringOrNull(aboutTag.getChildText("name", ns)));
    361         about.setDescription(Values.getStringOrNull(aboutTag.getChildText("description", ns)));
    362         about.setVersion(Values.getStringOrNull(aboutTag.getChildText("version", ns)));
    363         about.setContact(Values.getStringOrNull(aboutTag.getChildText("contact", ns)));
    364         about.setCopyright(Values.getStringOrNull(aboutTag.getChildText("copyright", ns)));
    365         about.setEmail(Values.getStringOrNull(aboutTag.getChildText("email", ns)));
    366         about.setUrl(Values.getStringOrNull(aboutTag.getChildText("url", ns)));
    367       }
     494      AboutBean about = loadAbout(epTag.getChild("about", ns));
     495      if (globalAbout != null)
     496      {
     497        if (about == null) about = new AboutBean();
     498        about.copy(globalAbout, false);
     499      }
     500      if (about != null) ext.setAbout(about);
    368501     
    369502      // Load and set ActionFactory (required)
     
    383516      }
    384517    }
     518    extensions.addAll(temp);
     519    return numLoaded;
    385520  }
    386521 
     
    398533    @return An initialised factory
    399534  */
    400   private <F> F createFactory(Element factoryTag, ClassLoader classLoader, Class<F> factoryType)
     535  protected <F> F createFactory(Element factoryTag, ClassLoader classLoader, Class<F> factoryType)
    401536    throws ClassNotFoundException, NoSuchMethodException,
    402537      IllegalAccessException, InstantiationException
     
    427562  */
    428563  @SuppressWarnings("unchecked")
    429   private void initBeanWithReflection(Object bean, Element root)
     564  protected void initBeanWithReflection(Object bean, Element root)
    430565  {
    431566    if (root == null) return;
    432567    Class<?> beanClass = bean.getClass();
    433     for (Element child : (List<Element>)root.getChildren())
    434     {
     568   
     569    List<Element> parameters = (List<Element>)root.getChildren();
     570    factoryParameters.put(bean, xmlOut.outputString(root));
     571   
     572    // List all child tags
     573    for (Element child : parameters)
     574    {
     575      // childName is the name of the setter method --> set<ChildName>
    435576      String childName = child.getName();
     577
     578      // The value passed as an argument to the setter method
    436579      String value = Values.getStringOrNull(child.getText());
    437       if (value != null)
    438       {
    439         String firstLetter = childName.substring(0, 1).toUpperCase();
    440         String methodName = "set" + firstLetter + childName.substring(1);
    441         try
     580      if (value == null) continue;
     581
     582      Method setter = getSetterMethod(beanClass, childName);
     583      if (setter == null) continue;
     584
     585      // Convert the input value
     586      if (converters != null)
     587      {
     588        for (ValueConverter converter : converters)
    442589        {
    443           Method setter = beanClass.getMethod(methodName, String.class);
    444          
    445           // Convert input values
    446           if (converters != null)
    447           {
    448             for (ValueConverter converter : converters)
    449             {
    450               value = converter.convert(value, setter);
    451             }
    452           }
    453           setter.invoke(bean, value);
     590          value = converter.convert(value, setter);
    454591        }
    455         catch (NoSuchMethodException ex)
    456         {}
    457         catch (IllegalAccessException ex)
    458         {}
    459         catch (InvocationTargetException ex)
    460         {}
    461       }
    462     }
    463   }
    464  
     592      }
     593
     594      // Invoke the setter method -- ignore all errors
     595      try
     596      {
     597        setter.invoke(bean, value);
     598      }
     599      catch (IllegalAccessException ex)
     600      {}
     601      catch (InvocationTargetException ex)
     602      {}
     603    }
     604  }
     605 
     606  /**
     607    Get the setter method for a given tag name.
     608    @param beanClass The class to look for the setter method in
     609    @param tagName The tag name in the XML file
     610    @return A Method object or null, if no method is found
     611  */
     612  protected Method getSetterMethod(Class<?> beanClass, String tagName)
     613  {
     614    String methodName = getSetterMethodNameFromTag(tagName);
     615    Method setter = null;
     616    try
     617    {
     618      setter = beanClass.getMethod(methodName, String.class);
     619    }
     620    catch (NoSuchMethodException ex)
     621    {}
     622    return setter;
     623  }
     624 
     625  /**
     626    Convert the tag name to a setter method name.
     627    @param tagName The tag name
     628    @return The tag name prefixed with 'set' and the first letter captialized
     629  */
     630  protected String getSetterMethodNameFromTag(String tagName)
     631  {
     632    String firstLetter = tagName.substring(0, 1).toUpperCase();
     633    String setterName = "set" + firstLetter + tagName.substring(1);
     634    return setterName;
     635  }
    465636
    466637}
  • trunk/src/test/TestExtensions.java

    r4187 r4198  
    102102      converter.setVariable("HOME", "~");
    103103      loader.addValueConverter(converter);
    104       loader.loadXmlFile(in, xmlFile, null);
     104      loader.loadXmlFile(in, xmlFile, null, true);
    105105     
    106106      List<ExtensionPoint<Action>> list = loader.getExtensionPoints();
     
    135135      converter.setVariable("HOME", "~");
    136136      loader.addValueConverter(converter);
    137       loader.loadXmlFile(in, xmlFile, null);
     137      loader.loadXmlFile(in, xmlFile, null, true);
    138138     
    139139      List<Extension<Action>> list = loader.getExtensions();
  • trunk/www/admin/extensions/details.jsp

    r4187 r4198  
    1 <%-- $Id$
     1<%-- $Id:details.jsp 4187 2008-03-20 11:15:25Z nicklas $
    22  ------------------------------------------------------------------
    33  Copyright (C) 2006 Nicklas Nordborg
     
    3737  import="net.sf.basedb.util.extensions.ExtensionPoint"
    3838  import="net.sf.basedb.util.extensions.Extension"
     39  import="net.sf.basedb.util.extensions.ActionFactory"
     40  import="net.sf.basedb.util.extensions.RendererFactory"
     41  import="net.sf.basedb.util.extensions.AboutBean"
    3942  import="net.sf.basedb.clients.web.extensions.ExtensionsControl"
     43  import="net.sf.basedb.clients.web.extensions.ExtensionsFile"
     44  import="net.sf.basedb.clients.web.extensions.ScanResults"
     45  import="net.sf.basedb.clients.web.formatter.FormatterFactory"
     46  import="net.sf.basedb.util.formatter.Formatter"
    4047  import="net.sf.basedb.core.plugin.About"
    4148  import="java.util.List"
     49  import="java.util.Date"
    4250  import="java.util.Iterator"
    4351%>
    4452<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
    4553<%@ taglib prefix="tbl" uri="/WEB-INF/table.tld" %>
     54<%!
     55String displayFactory(ExtensionsFile file, Object factory)
     56{
     57  if (factory == null)
     58  {
     59    return "<i>- none -</i>";
     60  }
     61  String text = factory.getClass().getName();
     62  String parameters = file.getFactoryParameters(factory);
     63  if (parameters != null)
     64  {
     65    text += "<pre>" + HTML.encodeTags(parameters) + "</pre>";
     66  }
     67  return text;
     68}
     69%>
    4670<%
    4771final SessionControl sc = Base.getExistingSessionControl(pageContext, true);
     
    5074String extensionId = request.getParameter("extensionId");
    5175String extensionPointId = request.getParameter("extensionPointId");
     76String filename = request.getParameter("filename");
    5277
    5378DbControl dc = sc.newDbControl();
     
    5984  Extension<?> ext = null;
    6085  ExtensionPoint<?> ep = null;
     86  ExtensionsFile file = null;
     87  if (filename != null)
     88  {
     89    file = ec.getFile(filename);
     90    if (file == null)
     91    {
     92      throw new ItemNotFoundException("ExtensionsFile[name=" + filename + "]");
     93    }
     94  }
    6195 
    6296  if (extensionId != null)
     
    68102    }
    69103    extensionPointId = ext.getExtends();
     104    //if (file == null) file = ec.getFileByExtensionId(extensionId);
    70105  }
    71106 
     
    77112      throw new ItemNotFoundException("ExtensionPoint[id=" + extensionPointId + "]");
    78113    }
    79   }
    80  
    81   %>
     114    //if (file == null) file = ec.getFileByExtensionId(extensionPointId);
     115  }
     116%>
    82117<base:page title="" type="popup">
    83118  <base:head scripts="newjoust.js,table.js" styles="newjoust.css,table.css,toolbar.css">
     
    103138    location.href = url;
    104139  }
     140 
     141  function enableFile(enable)
     142  {
     143    window.parent.frames['tree'].setChildIcons('<%=HTML.javaScriptEncode(filename)%>', enable);
     144    var url = 'index.jsp?ID=<%=ID%>&cmd=EnableFile';
     145    url += '&filename=<%=HTML.urlEncode(filename)%>';
     146    url += '&enable='+enable;
     147    location.href = url;
     148  }
     149 
     150  function showFile(filename)
     151  {
     152    window.parent.frames['tree'].selectFile(filename);
     153  }
    105154
    106155  function editSettings()
     
    111160  function manualScan()
    112161  {
    113     Main.openPopup('manual_scan.jsp?ID=<%=ID%>', 'ManualScan', 500, 400);
     162    Main.openPopup('index.jsp?ID=<%=ID%>&cmd=ManualScan', 'ManualScan', 600, 480);
     163  }
     164 
     165  function scanResults()
     166  {
     167    Main.openPopup('index.jsp?ID=<%=ID%>&cmd=ScanResults', 'ScanResults', 600, 480);
    114168  }
    115169  </script>
     
    131185        disabled="<%=!writePermission%>"
    132186      />
     187      <tbl:button
     188        image="help.gif"
     189        onclick="<%="Main.openHelp('" + ID +"', 'extensions.details.extension')"%>"
     190        title="Help&hellip;"
     191        tooltip="Get help about this page"
     192      />
    133193      <%
    134194    }
     
    143203        tooltip="Disable this extension point"
    144204        disabled="<%=!writePermission%>"
    145        
     205      />
     206      <tbl:button
     207        image="help.gif"
     208        onclick="<%="Main.openHelp('" + ID +"', 'extensions.details.extensionspoint')"%>"
     209        title="Help&hellip;"
     210        tooltip="Get help about this page"
     211      />
     212      <%
     213    }
     214    else if (file != null)
     215    {
     216      %>
     217      <tbl:button
     218        onclick="enableFile(1)"
     219        title="Enable all"
     220        image="<%=writePermission ? "joust/extension.png" : "joust/extensiondisabled.png" %>"
     221        tooltip="Enable all extensions in this file"
     222        disabled="<%=!writePermission%>"
     223      />
     224      <tbl:button
     225        onclick="enableFile(0)"
     226        title="Disable all"
     227        image="<%=writePermission ? "joust/extension.png" : "joust/extensiondisabled.png" %>"
     228        tooltip="Disable all extensions in this file"
     229        disabled="<%=!writePermission%>"
    146230      />
    147231      <%
     
    164248        disabled="<%=!writePermission%>"
    165249      />
     250      <tbl:button
     251        image="help.gif"
     252        onclick="<%="Main.openHelp('" + ID +"', 'extensions.details.main')"%>"
     253        title="Help&hellip;"
     254        tooltip="Get help about this page"
     255      />
    166256      <%
    167257    }
     
    169259    </tbl:toolbar>
    170260
    171     <div class="boxedbottom"
     261    <div class="boxedbottom">
    172262   
    173263    <%
     
    175265    {
    176266      About about = ext.getAbout();
     267      if (about == null) about = new AboutBean();
     268      ExtensionsFile extFile = ec.getFileByExtensionId(ext.getId());
    177269      %>
    178270      </a>
     
    184276        <tr>
    185277          <td class="prompt">Name</td>
    186           <td><%=HTML.encodeTags(about == null ? "" : about.getName())%></td>
     278          <td><%=HTML.encodeTags(about.getName())%></td>
     279        </tr>
     280        <tr>
     281          <td class="prompt">File</td>
     282          <td>
     283            <a href="javascript:showFile('<%=HTML.javaScriptEncode(extFile.getName())%>')"
     284            ><%=extFile.getName()%></a> (<%=extFile.isModified() ? "Modified" : "Up to date" %>)
     285          </td>
    187286        </tr>
    188287        <tr>
    189288          <td class="prompt">Version</td>
    190           <td><%=HTML.encodeTags(about == null ? "" : about.getVersion())%></td>
     289          <td><%=HTML.encodeTags(about.getVersion())%></td>
    191290        </tr>
    192291        <tr>
    193292          <td class="prompt">Description</td>
    194           <td><%=HTML.niceFormat(about == null ? "" : about.getDescription())%></td>
     293          <td><%=HTML.niceFormat(about.getDescription())%></td>
    195294        </tr>
    196295        <tr>
    197296          <td class="prompt">Copyright</td>
    198           <td><%=HTML.encodeTags(about == null ? "" : about.getCopyright())%></td>
     297          <td><%=HTML.encodeTags(about.getCopyright())%></td>
    199298        </tr>
    200299        <tr>
    201300          <td class="prompt">Contact</td>
    202           <td><%=HTML.encodeTags(about == null ? "" : about.getContact())%></td>
     301          <td><%=HTML.encodeTags(about.getContact())%></td>
    203302        </tr>
    204303        <tr>
     
    206305          <td><%=HTML.scanForLinks(about.getEmail(), HTML.LINK_EMAIL, "_new")%></td>
    207306        </tr>
    208           <tr>
     307        <tr>
    209308          <td class="prompt">Url</td>
    210309          <td><%=HTML.scanForLinks(about.getUrl(), HTML.LINK_URL, "_new")%></td>
     
    213312          <tr>
    214313          <td class="prompt">Action factory</td>
    215           <td><%=ext.getActionFactory()%></td>
     314          <td><%=displayFactory(extFile, ext.getActionFactory())%></td>
    216315        </tr>
    217316        </tr>
    218317          <tr>
    219318          <td class="prompt">Renderer factory</td>
    220           <td><%=ext.getRendererFactory()%></td>
     319          <td><%=displayFactory(extFile, ext.getRendererFactory())%></td>
    221320        </tr>
    222321        </table>
     
    224323    }
    225324    %>
    226    
    227325    <%
    228326    if (ep != null)
    229327    {
    230       %>
    231       <%
     328      ExtensionsFile epFile = ec.getFileByExtensionId(ep.getId());
    232329      if (ext != null)
    233330      {
     
    247344        </tr>
    248345        <tr>
     346          <td class="prompt">File</td>
     347          <td>
     348            <a href="javascript:showFile('<%=HTML.javaScriptEncode(epFile.getName())%>')"
     349            ><%=epFile.getName()%></a> (<%=epFile.isModified() ? "Modified" : "Up to date" %>)
     350          </td>
     351        </tr>
     352        <tr>
    249353          <td class="prompt">Description</td>
    250354          <td><%=HTML.niceFormat(ep.getDescription())%></td>
     
    252356        <tr>
    253357          <td class="prompt">Action class</td>
    254           <td><%=ep.getActionClass()%></td>
     358          <td><%=ep.getActionClass().getName()%></td>
    255359        </tr>
    256360        <tr>
    257361          <td class="prompt">Renderer factory</td>
    258           <td><%=ep.getRendererFactory()%></td>
     362          <td><%=displayFactory(epFile, ep.getRendererFactory())%></td>
    259363        </tr>
    260364        </table>
     
    264368     
    265369      <%
    266       if (ext == null && ep == null)
     370      if (file != null)
    267371      {
    268         int autoInstall = ec.getAutoInstall();
     372        About about = file.getAbout();
     373        if (about == null) about = new AboutBean();
    269374        %>
    270375        <table class="form" cellspacing="0">
    271376        <tr>
    272           <td class="prompt">Auto-installation</td>
    273           <td><%=autoInstall > 0 ? "Check every " + autoInstall + " seconds" : "Disabled" %></td>
     377          <td class="prompt">File</td>
     378          <td><%=file.getName()%></td>
     379        </tr>
     380        <tr>
     381          <td class="prompt">Type</td>
     382          <td><%=file.isJar() ? "JAR file" : "XML file"%></td>
     383        </tr>
     384        <tr>
     385          <td class="prompt">Up to date</td>
     386          <td><%=file.isModified() ? "No" : "Yes"%></td>
     387        </tr>
     388        <tr>
     389          <td class="prompt">Name</td>
     390          <td><%=HTML.encodeTags(about.getName())%></td>
     391        </tr>
     392        <tr>
     393          <td class="prompt">Version</td>
     394          <td><%=HTML.encodeTags(about.getVersion())%></td>
     395        </tr>
     396        <tr>
     397          <td class="prompt">Description</td>
     398          <td><%=HTML.niceFormat(about.getDescription())%></td>
     399        </tr>
     400        <tr>
     401          <td class="prompt">Copyright</td>
     402          <td><%=HTML.encodeTags(about.getCopyright())%></td>
     403        </tr>
     404        <tr>
     405          <td class="prompt">Contact</td>
     406          <td><%=HTML.encodeTags(about.getContact())%></td>
     407        </tr>
     408        <tr>
     409          <td class="prompt">Email</td>
     410          <td><%=HTML.scanForLinks(about.getEmail(), HTML.LINK_EMAIL, "_new")%></td>
     411        </tr>
     412        <tr>
     413          <td class="prompt">Url</td>
     414          <td><%=HTML.scanForLinks(about.getUrl(), HTML.LINK_URL, "_new")%></td>
    274415        </tr>
    275416        </table>
     
    277418      }
    278419      %>
    279    
     420      <%
     421      if (ext == null && ep == null && file == null)
     422      {
     423        int autoInstall = ec.getAutoInstall();
     424        ScanResults results = ec.getLastScanResults();
     425        Formatter dateTimeFormatter = FormatterFactory.getDateTimeFormatter(sc);
     426        %>
     427        <table class="form" cellspacing="0">
     428        <%
     429        if (autoInstall > 0)
     430        {
     431          %>
     432          <tr>
     433            <td class="prompt">Automatic scan</td>
     434            <td>
     435              Every <%=autoInstall%> seconds
     436            </td>
     437          </tr>
     438          <tr>
     439            <td class="prompt" style="text-align: right; font-weight: normal;">- next scan</td>
     440            <td><%=dateTimeFormatter.format(new Date(ec.getNextAutoScanTime())) %></td>
     441          </tr>
     442          <%
     443        }
     444        else
     445        {
     446          %>
     447          <tr>
     448            <td class="prompt">Automatic scan</td>
     449            <td>Disabled</td>
     450          </tr>
     451          <%
     452        }
     453        %>
     454        <tr>
     455          <td class="prompt">Last scan</td>
     456          <td><%=results.hasError() ? "Failed": "Successful" %></td>
     457        </tr>
     458        <tr>
     459          <td class="prompt" style="text-align: right; font-weight: normal;">- ended</td>
     460          <td><%=dateTimeFormatter.format(new Date(results.getEndTime())) %></td>
     461        </tr>
     462        <tr>
     463          <td class="prompt" style="text-align: right; font-weight: normal;">- summary</td>
     464          <td>
     465          <%=HTML.niceFormat(results.getSummary())%></td>
     466        </tr>
     467        <tr>
     468          <td  class="prompt" style="text-align: right; font-weight: normal;"><base:icon image="bullet.gif" /></td>
     469          <td>
     470            <a href="javascript:scanResults()"
     471              title="Display detailed information about the last scan"
     472              >More details&hellip;</a>
     473          </td>
     474        </tr>
     475        </table>
     476        <%
     477      }
     478      %>
    280479      </div>
    281480    </base:body>
  • trunk/www/admin/extensions/index.jsp

    r4187 r4198  
    1 <%-- $Id$
     1<%-- $Id:index.jsp 4187 2008-03-20 11:15:25Z nicklas $
    22  ------------------------------------------------------------------
    33  Copyright (C) 2006 Jari Hakkinen, Nicklas Nordborg
     
    3636  import="net.sf.basedb.util.extensions.Extension"
    3737  import="net.sf.basedb.clients.web.extensions.ExtensionsControl"
     38  import="net.sf.basedb.clients.web.extensions.ExtensionsFile"
     39  import="java.util.List"
    3840%>
    3941<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
    4042<%
    41 final SessionControl sc = Base.getExistingSessionControl(pageContext, true);
     43  final SessionControl sc = Base.getExistingSessionControl(pageContext, true);
    4244final String ID = sc.getId();
    4345final String cmd = request.getParameter("cmd");
     
    8284    redirect = "details.jsp?ID=" + ID + "&extensionPointId=" + extensionPointId;
    8385  }
     86  else if ("EnableFile".equals(cmd))
     87  {
     88    dc = sc.newDbControl();
     89    String filename = request.getParameter("filename");
     90    boolean enable = Values.getBoolean(request.getParameter("enable"));
     91    ExtensionsControl ec = ExtensionsControl.get(dc);
     92    ec.enableAllInFile(filename, enable);
     93    ec.saveSettings();
     94    redirect = "details.jsp?ID=" + ID + "&filename=" + HTML.urlEncode(filename);
     95  }
    8496  else if ("ManualScan".equals(cmd))
     97  {
     98    forward = "manual_scan.jsp?ID=" + ID;
     99  }
     100  else if ("DoManualScan".equals(cmd))
    85101  {
    86102    dc = sc.newDbControl();
     
    88104    boolean forceUpdate = Values.getBoolean(request.getParameter("forceUpdate"));
    89105    ec.installAndUpdateExtensions(forceUpdate);
    90     message = "Extensions reloaded";
     106    redirect = "index.jsp?ID=" + ID + "&cmd=ScanResults";
     107  }
     108  else if ("ScanResults".equals(cmd))
     109  {
     110    forward = "scan_results.jsp?ID=" + ID;
    91111  }
    92112  else
  • trunk/www/admin/extensions/manual_scan.jsp

    r4187 r4198  
    1 <%-- $Id$
     1<%-- $Id:manual_scan.jsp 4187 2008-03-20 11:15:25Z nicklas $
    22  ------------------------------------------------------------------
    33  Copyright (C) 2005 Nicklas Nordborg
     
    6868
    6969  <form action="index.jsp?ID=<%=ID%>" method="post" name="scan" onsubmit="return false;">
    70   <input type=hidden name="cmd" value="ManualScan">
     70  <input type=hidden name="cmd" value="DoManualScan">
    7171 
    7272  <h3 class="docked">Perform manual scan <base:help helpid="extensions.manualscan" /></h3>
     
    7575    <div class="helpmessage">
    7676    A manual scan will check the extensions directory for new, updated
    77     or deleted extensions. If <b>Update unchanged</b> is selected, extensions
     77    or deleted extensions. If <b>Force update</b> is selected, extensions
    7878    that are unchanged will also be updated.
    7979    </div>
     
    8181    <table class="form" cellspacing=0>
    8282    <tr>
    83       <td class="prompt">Update unchanged</td>
     83      <td class="prompt">Force update</td>
    8484      <td>
    8585        <input type="checkbox" class="text" name="forceUpdate" value="1">
  • trunk/www/admin/extensions/tree.jsp

    r4187 r4198  
    1 <%-- $Id$
     1<%-- $Id:tree.jsp 4187 2008-03-20 11:15:25Z nicklas $
    22  ------------------------------------------------------------------
    33  Copyright (C) 2006 Nicklas Nordborg
     
    3535  import="net.sf.basedb.util.extensions.ExtensionPoint"
    3636  import="net.sf.basedb.util.extensions.Extension"
     37  import="net.sf.basedb.util.extensions.Action"
    3738  import="net.sf.basedb.clients.web.extensions.ExtensionsControl"
     39  import="net.sf.basedb.clients.web.extensions.ExtensionsFile"
    3840  import="net.sf.basedb.core.plugin.About"
    3941  import="java.util.List"
     
    4143%>
    4244<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
     45<%!
     46String getJoustExtensionPoint(String parentNode, ExtensionsControl ec, ExtensionPoint ep)
     47{
     48  String id = ep.getId();
     49  String name = ep.getName();
     50  if (name == null) name = id;
     51  String icon = ec.isEnabled(ep) ? "ExtensionPoint" : "ExtensionPointDisabled";
     52  String joust = "ep = JoustMenu.addChildItem(" + parentNode +", '" + icon + "', " +
     53      "'" + HTML.javaScriptEncode(name) + "', 'extensionPointOnClick(\"" + id + "\")', " +
     54      "'', '" + id + "')";
     55  return joust;
     56}
     57String getJoustExtension(String parentNode, ExtensionsControl ec, Extension ext)
     58{
     59  String id = ext.getId();
     60  About about = ext.getAbout();
     61  String name = about == null || about.getName() == null ? id : about.getName();
     62  String icon = ec.isEnabled(ext) ? "Extension" : "ExtensionDisabled";
     63  String joust = "ext = JoustMenu.addChildItem(" + parentNode + ", '" + icon + "', " +
     64      "'" + HTML.javaScriptEncode(name) + "', 'extensionOnClick(\"" + id + "\")', " +
     65      "'', '" + id + "')";
     66  return joust;
     67}
     68%>
    4369<%
    4470final SessionControl sc = Base.getExistingSessionControl(pageContext, true);
     
    6894    IconStore.addIcon('ExtensionDisabled', path + 'extensiondisabled.png', 16, 16);
    6995    IconStore.addIcon('ExtensionDisabledSelected', path + 'extensiondisabledselected.png', 16, 16);
    70 
    71     var rootNode = JoustMenu.addMenuItem(-1, 'Root', 'Extension points & extensions', 'rootOnClick()', '', '');
     96    IconStore.addIcon('XmlFile', path + 'item.gif', 18, 16);
     97    IconStore.addIcon('XmlFileSelected', path + 'itemselected.gif', 18, 16);
     98    IconStore.addIcon('JarFile', path + 'jarfile.png', 18, 16);
     99    IconStore.addIcon('JarFileSelected', path + 'jarfileselected.png', 18, 16);
     100
     101    var byExtPoint = JoustMenu.addMenuItem(-1, 'Root', 'By extension point', 'rootOnClick()', '', '');
    72102    var ep;
    73103    var ext;
     
    78108    {
    79109      ExtensionPoint ep = extensionPoints.next();
    80       String epId = ep.getId();
    81       String epName = ep.getName();
    82       if (epName == null) epName = epId;
    83       String icon = ec.isEnabled(ep) ? "ExtensionPoint" : "ExtensionPointDisabled";
    84110      %>
    85       ep = JoustMenu.addChildItem(rootNode, '<%=icon%>', '<%=HTML.javaScriptEncode(epName)%>', "extensionPointOnClick('<%=epId%>')", '', '<%=epId%>');
     111      <%=getJoustExtensionPoint("byExtPoint", ec, ep)%>
    86112      <%
    87       Iterator<Extension<?>> extensions = ec.getExtensions(epId);
     113      Iterator<Extension<?>> extensions = ec.getExtensions(ep.getId());
    88114      while (extensions.hasNext())
    89115      {
    90116        Extension ext = extensions.next();
    91         String extId = ext.getId();
    92         About about = ext.getAbout();
    93         String extName = about == null ? extId : about.getName();
    94         icon = ec.isEnabled(ext) ? "Extension" : "ExtensionDisabled";
    95117        %>
    96         ext = JoustMenu.addChildItem(ep, '<%=icon%>', '<%=HTML.javaScriptEncode(extName)%>', "extensionOnClick('<%=extId%>')", '', '<%=extId%>');
     118        <%=getJoustExtension("ep", ec, ext)%>
    97119        <%
    98120      }
    99      
    100121    }
    101122    %>
    102    
    103     JoustMenu.menuItems[rootNode].isOpen = true;
     123    var byFile = JoustMenu.addMenuItem(-1, 'Root', 'By file', 'rootOnClick()');
     124    var file;
     125    <%
     126    Iterator<ExtensionsFile> files = ec.getFiles();
     127    while (files.hasNext())
     128    {
     129      ExtensionsFile ef = files.next();
     130      String efName = ef.getName();
     131      String icon = ef.isJar() ? "JarFile" : "XmlFile";
     132      String id = efName;
     133      %>
     134      file = JoustMenu.addChildItem(byFile, '<%=icon%>', '<%=HTML.javaScriptEncode(efName)%>', 'fileOnClick("<%=efName%>")', '', '<%=id%>');
     135      <%
     136      Iterator<ExtensionPoint<?>> eps = ef.getExtensionPoints();
     137      while (eps.hasNext())
     138      {
     139        ExtensionPoint ep = eps.next();
     140        %>
     141        <%=getJoustExtensionPoint("file", ec, ep)%>
     142        <%
     143      }
     144      Iterator<Extension<?>> exts = ef.getExtensions();
     145      while (exts.hasNext())
     146      {
     147        Extension ext = exts.next();
     148        %>
     149        <%=getJoustExtension("file", ec, ext)%>
     150        <%
     151      }
     152    }
     153    %>
     154    JoustMenu.menuItems[byExtPoint].isOpen = true;
     155    JoustMenu.menuItems[byFile].isOpen = true;
    104156    JoustMenu.draw('joust');
    105157    isInitialised = true;
     
    118170  function setIcon(nodeId, iconName)
    119171  {
    120     var node = JoustMenu.menuItems[nodeId];
    121     if (node)
    122     {
    123       node.iconName = iconName;
    124       JoustMenu.updateIconsAndText(node.index);
     172    var icons = new Array();
     173    icons[nodeId] = iconName;
     174    setIconsByExternalId(icons);
     175  }
     176 
     177  function setChildIcons(parentNodeId, enable)
     178  {
     179    var node = JoustMenu.menuItems[parentNodeId];
     180    if (!node) return;
     181    var childNode = JoustMenu.menuItems[node.firstChildIndex];
     182    var icons = new Array();
     183    var numChildren = 0;
     184    while (childNode)
     185    {
     186      numChildren++;
     187      var iconName = childNode.iconName;
     188      var isDisabled = iconName.indexOf('Disabled') > 0;
     189      if (enable && isDisabled)
     190      {
     191        iconName = iconName.replace('Disabled', '');
     192      }
     193      else if (!enable && !isDisabled)
     194      {
     195        iconName += 'Disabled';
     196      }
     197      icons[childNode.externalId] = iconName;
     198      childNode = JoustMenu.menuItems[childNode.nextItemIndex];
     199    }
     200    if (numChildren > 0) setIconsByExternalId(icons);
     201  }
     202 
     203  function setIconsByExternalId(icons)
     204  {
     205    for (var i = 0; i < JoustMenu.menuItems.length; i++)
     206    {
     207      var node = JoustMenu.menuItems[i];
     208      var iconName = icons[node.externalId];
     209      if (iconName)
     210      {
     211        node.iconName = iconName;
     212        JoustMenu.updateIconsAndText(node.index);
     213      }
     214    }
     215  }
     216 
     217  function selectFile(filename)
     218  {
     219    var fileNode = JoustMenu.menuItems[filename];
     220    if (fileNode)
     221    {
     222      JoustMenu.open(fileNode.index);
     223      JoustMenu.select(fileNode.index);
    125224    }
    126225  }
     
    139238    parent.frames['details'].location.href = 'details.jsp?ID=<%=ID%>&extensionId='+extensionId;
    140239  }
    141  
     240  function fileOnClick(filename)
     241  {
     242    parent.frames['details'].location.href = 'details.jsp?ID=<%=ID%>&filename='+escape(filename);
     243  }
    142244  </script>
    143245  </base:head>
Note: See TracChangeset for help on using the changeset viewer.