Changeset 4618


Ignore:
Timestamp:
Oct 30, 2008, 11:41:41 AM (13 years ago)
Author:
Nicklas Nordborg
Message:

Fixes #1161: Improve error handling in the extension system

Location:
trunk
Files:
1 added
12 edited

Legend:

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

    r4512 r4618  
    444444
    445445  /**
     446    Get information about the last error that happened
     447    when rendering an extension point.
     448    @param id The id of the extension point
     449    @return The error, or null if no error information is available
     450    @since 2.9
     451  */
     452  public Throwable getLastExtensionPointError(String id)
     453  {
     454    return registry.getLastExtensionPointError(id);
     455  }
     456
     457  /**
    446458    Enable/disable an extension point.
    447459    @param extensionPointId The ID of the extension point to enable/disable
     
    494506  {
    495507    return registry.getExtension(id);
     508  }
     509 
     510  /**
     511    Get information about the last error that happened
     512    when rendering an extension.
     513    @param id The id of the extension
     514    @return The error, or null if no error information is available
     515    @since 2.9
     516  */
     517  public Throwable getLastExtensionError(String id)
     518  {
     519    return registry.getLastExtensionError(id);
    496520  }
    497521 
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/service/Services.java

    r4512 r4618  
    115115    ExtensionsFilter filter = extension == null ?
    116116        settings : new SingleExtensionFilter(extension.getId());
    117     return (ExtensionsInvoker<ServiceControllerAction>)ExtensionsControl.useExtensions(null, filter, EXTENSION_POINT_ID);
     117    ExtensionsInvoker<?> invoker = ExtensionsControl.useExtensions(null, filter, EXTENSION_POINT_ID);
     118    invoker.setClearErrors(true);
     119    return (ExtensionsInvoker<ServiceControllerAction>)invoker;
    118120  }
    119121 
  • trunk/src/core/net/sf/basedb/util/extensions/ActionIterator.java

    r4515 r4618  
    6060  // The current context
    6161  private InvokationContext<A> currentContext;
     62  private A currentAction;
    6263 
    6364  // An iterator over all usable extensions
     
    129130    if (!hasNext) throw new NoSuchElementException();
    130131    checkNext = true;
    131     return actions[offset];
     132    currentAction = actions[offset];
     133    return currentAction;
    132134  }
    133135  /**
     
    160162  /**
    161163    Gets the renderer for the current action.
    162   */
    163   Renderer<? super A> getRenderer()
     164    @since 2.9
     165  */
     166  public Renderer<? super A> getRenderer()
    164167  {
    165168    return currentContext.getRenderer();
    166169  }
    167170
     171  /**
     172    Clear any error that has been registered for the current
     173    action.
     174    @since 2.9
     175    @see #setError(Throwable)
     176  */
     177  public void clearError()
     178  {
     179    currentContext.clearError();
     180  }
     181 
     182  /**
     183    Register an error for the current action. The error can be retreived
     184    with {@link Registry#getLastExtensionError(String)}.
     185    @param t The error to register
     186    @since 2.9
     187  */
     188  public void setError(Throwable t)
     189  {
     190    currentContext.setError(currentAction, t);
     191  }
     192 
    168193  /**
    169194    Check that the actions array is a not-null, non-empty array
     
    190215        Extension ext = getExtension();
    191216        ClassCastException cc = new ClassCastException(actions[i] + " -> " + actionClass.getName());
     217        currentContext.setError(actions[i], cc);
    192218        log.error("Action '" + actions[i] + "' created by extension '" + ext.getId() +
    193219          "' is not of the expected class '" + actionClass.getName(), cc);
  • trunk/src/core/net/sf/basedb/util/extensions/ExtensionContext.java

    r4515 r4618  
    2222package net.sf.basedb.util.extensions;
    2323
     24import net.sf.basedb.util.extensions.Registry.RegisteredExtension;
     25
    2426/**
    2527  Invokation context for an extension. Most
     
    3537{
    3638  private final ExtensionPointContext<? super A> mainContext;
    37   private final Extension<A> extension;
     39  private final RegisteredExtension<A> extension;
    3840  private Renderer<? super A> renderer;
    3941  private boolean hasCreatedRenderer;
    4042 
    41   ExtensionContext(ExtensionPointContext<? super A> mainContext, Extension<A> extension)
     43  ExtensionContext(ExtensionPointContext<? super A> mainContext, RegisteredExtension<A> extension)
    4244  {
    4345    this.mainContext = mainContext;
     
    9092      catch (Throwable t)
    9193      {
     94        setError(null, t);
    9295        log.error("Error preparing renderer factory for extension '" +
    9396          extension.getId() + "': " + factory, t);
     
    140143    catch (Throwable t)
    141144    {
     145      setError(null, t);
    142146      log.error("Error creating actions for extension '" +
    143147          extension.getId() + "' using factory: " + factory, t);     
    144148    }
    145149    return actions;
     150  }
     151 
     152  @Override
     153  protected void setError(A action, Throwable t)
     154  {
     155    extension.setError(action, t);
     156  }
     157 
     158  @Override
     159  protected void clearError()
     160  {
     161    extension.clearError();
    146162  }
    147163  // ----------------------------------------
  • trunk/src/core/net/sf/basedb/util/extensions/ExtensionPointContext.java

    r4515 r4618  
    2222package net.sf.basedb.util.extensions;
    2323
     24import net.sf.basedb.util.extensions.Registry.RegisteredExtensionPoint;
     25
    2426/**
    2527  Invokation context for an extension point.
     
    3537  private final ClientContext clientContext;
    3638  private final Registry registry;
    37   private final ExtensionPoint<? super A> extensionPoint;
     39  private final RegisteredExtensionPoint<? super A> extensionPoint;
    3840  private Renderer<? super A> renderer;
    3941  private boolean hasCreatedRenderer;
    4042 
    4143  ExtensionPointContext(Registry registry, ClientContext clientContext,
    42       ExtensionPoint<? super A> extensionPoint)
     44      RegisteredExtensionPoint<? super A> extensionPoint)
    4345  {
    4446    this.registry = registry;
     
    99101      catch (Throwable t)
    100102      {
     103        setError(null, t);
    101104        log.error("Error preparing renderer factory for extension point '" +
    102105          extensionPoint.getId() + "': " + factory, t);
     
    133136    return null;
    134137  }
     138 
     139  @Override
     140  protected void setError(A action, Throwable t)
     141  {
     142    extensionPoint.setError(action, t);
     143  }
     144 
     145  @Override
     146  protected void clearError()
     147  {
     148    extensionPoint.clearError();
     149  }
    135150  // ----------------------------------------
    136151
  • trunk/src/core/net/sf/basedb/util/extensions/ExtensionsInvoker.java

    r4515 r4618  
    2323
    2424import java.util.Collection;
    25 import java.util.Iterator;
    2625
    2726/**
     
    5857    org.apache.log4j.LogManager.getLogger("net.sf.basedb.util.extensions.ExtensionsInvoker");
    5958 
    60   final Collection<InvokationContext<A>> contexts;
     59  private final Collection<InvokationContext<A>> contexts;
     60  private boolean clearErrors;
    6161 
    6262  /**
     
    6868  }
    6969 
     70  /**
     71    Set a flag to indicate if existing errors should be cleared
     72    before an action is rendered. If this setting is not enabled (default)
     73    old errors are never cleared.
     74    @param clearErrors TRUE to clear old errors, FALSE to keep them
     75    @since 2.9
     76  */
     77  public void setClearErrors(boolean clearErrors)
     78  {
     79    this.clearErrors = clearErrors;
     80  }
     81
    7082  /**
    7183    Create an iterator that iterates over all {@link Action}:s created
     
    95107      try
    96108      {
     109        if (clearErrors) it.clearError();
    97110        action = it.next();
    98111        renderer = it.getRenderer();
     
    105118      catch (Throwable t)
    106119      {
     120        it.setError(t);
    107121        log.error("Could not render action '" + action + "' with renderer '" +
    108122            renderer + "'", t);
     
    121135  public void render(Renderer<A> renderer)
    122136  {
    123     Iterator<A> it = iterate();
     137    ActionIterator<A> it = iterate();
    124138    while (it.hasNext())
    125139    {
     
    127141      try
    128142      {
     143        if (clearErrors) it.clearError();
    129144        action = it.next();
    130145        renderer.render(action);
     
    132147      catch (Throwable t)
    133148      {
     149        it.setError(t);
    134150        log.error("Could not render action '" + action + "' with renderer '" +
    135151            renderer + "'", t);
  • trunk/src/core/net/sf/basedb/util/extensions/InvokationContext.java

    r4515 r4618  
    111111  protected abstract A[] getActions();
    112112 
     113  /**
     114    Register an error for the current extension or extension point.
     115    Registered errors can be retrieved by {@link Registry#getLastExtensionError(String)}
     116    and {@link Registry#getLastExtensionPointError(String)}
     117    @param action The action that caused the error
     118    @param t The error
     119    @since 2.9
     120  */
     121  protected abstract void setError(A action, Throwable t);
     122 
     123  /**
     124    Clear the registered error for the current extension or extension point.
     125    @since 2.9
     126  */
     127  protected abstract void clearError();
     128 
    113129}
  • trunk/src/core/net/sf/basedb/util/extensions/Registry.java

    r4515 r4618  
    309309 
    310310  /**
     311    Get information about the last error that happened
     312    when rendering an extension point.
     313    @param id The id of the extension point
     314    @return The error, or null if no error information is available
     315    @since 2.9
     316  */
     317  public Throwable getLastExtensionPointError(String id)
     318  {
     319    RegisteredExtensionPoint<?> rep = extensionPoints.get(id);
     320    return rep == null ? null : rep.getLastError();
     321  }
     322 
     323  /**
    311324    Register an extension. If the extension is already registered,
    312325    the description, position, action factory and renderer factory
     
    393406    return extensions.get(id);
    394407  }
     408 
     409  /**
     410    Get information about the last error that happened
     411    when rendering an extension.
     412    @param id The id of the extension
     413    @return The error, or null if no error information is available
     414    @since 2.9
     415  */
     416  public Throwable getLastExtensionError(String id)
     417  {
     418    RegisteredExtension<?> ext = extensions.get(id);
     419    return ext == null ? null : ext.getLastError();
     420  }
     421
    395422 
    396423  /**
     
    555582    private boolean allowRendererOverride;
    556583   
     584    private Throwable lastError;
     585    private A lastErrorAction;
     586   
    557587    /**
    558588      Create a new registered extension point by copying the
     
    647677    {
    648678      return this.extensions.values();
    649     }
     679    }
     680   
     681    synchronized void setError(A action, Throwable t)
     682    {
     683      this.lastErrorAction = action;
     684      this.lastError = t;
     685    }
     686   
     687    synchronized void clearError()
     688    {
     689      this.lastError = null;
     690      this.lastErrorAction = null;
     691    }
     692   
     693    Throwable getLastError()
     694    {
     695      return lastError;
     696    }
    650697  }
    651698   
     
    665712    private RendererFactory<? super A> rendererFactory;
    666713
     714    private Throwable lastError;
     715    private A lastErrorAction;
     716   
    667717    /**
    668718      Create a new registered extension by copying the
     
    756806      return rep;
    757807    }
     808
     809    synchronized void setError(A action, Throwable t)
     810    {
     811      this.lastErrorAction = action;
     812      this.lastError = t;
     813    }
     814   
     815    synchronized void clearError()
     816    {
     817      this.lastError = null;
     818      this.lastErrorAction = null;
     819    }
     820   
     821    Throwable getLastError()
     822    {
     823      return lastError;
     824    }
    758825  }   
    759826   
  • trunk/www/admin/extensions/details.jsp

    r4510 r4618  
    3333  import="net.sf.basedb.util.Values"
    3434  import="net.sf.basedb.core.ItemNotFoundException"
     35  import="net.sf.basedb.util.error.ThrowableUtil"
    3536  import="net.sf.basedb.util.extensions.ExtensionPoint"
    3637  import="net.sf.basedb.util.extensions.Extension"
     
    169170    Main.openPopup('index.jsp?ID=<%=ID%>&cmd=ScanResults', 'ScanResults', 600, 480);
    170171  }
     172
     173  function toggleStacktrace(evt, id)
     174  {
     175    Main.showHide('stacktrace.' + id);
     176  }
     177 
    171178  </script>
    172179  </base:head>
     
    179186    {
    180187      boolean isEnabled = ec.isEnabled(ext);
    181       boolean hasError = extFile != null && extFile.hasError();
    182       boolean allow = writePermission && !hasError;
     188      boolean hasRegistrationError = extFile != null && extFile.hasError();
     189      boolean allow = writePermission && !hasRegistrationError;
    183190      %>
    184191      <tbl:button
     
    200207    {
    201208      boolean isEnabled = ec.isEnabled(ep);
    202       boolean hasError = epFile != null && epFile.hasError();
    203       boolean allow = writePermission && !hasError;
     209      boolean hasRegistrationError = epFile != null && epFile.hasError();
     210      boolean allow = writePermission && !hasRegistrationError;
    204211      %>
    205212      <tbl:button
     
    220227    else if (file != null)
    221228    {
    222       boolean hasError = file.hasError();
    223       boolean allow = writePermission && !hasError;
     229      boolean hasRegistrationError = file.hasError();
     230      boolean allow = writePermission && !hasRegistrationError;
    224231      %>
    225232      <tbl:button
     
    274281      About about = ext.getAbout();
    275282      if (about == null) about = new AboutBean();
    276       %>
    277       </a>
     283      Throwable error = ec.getLastExtensionError(ext.getId());
     284      if (error != null)
     285      {
     286        %>
     287          <div class="error">
     288            <base:icon image="error.gif"
     289              onclick="<%="toggleStacktrace(event, '" + ext.getId() + "')"%>"
     290              tooltip="Toggle display of detailed stacktrace"
     291              style="float: left;"
     292              tooltip="Error - click to show full stack trace"
     293            /><%=error.getMessage() %>
     294            <div id="stacktrace.<%=ext.getId()%>"
     295              class="stacktrace"
     296              style="display: none;"
     297              ><%=ThrowableUtil.stackTraceToString(error)%></div>
     298          </div>
     299          <%
     300      }
     301      %>
    278302        <table class="form" cellspacing="0">
    279303        <tr>
     
    340364        <h4>Extension point</h4>
    341365        <%
     366      }
     367      Throwable error = ec.getLastExtensionPointError(ep.getId());
     368      if (error != null)
     369      {
     370        %>
     371          <div class="error">
     372            <base:icon image="error.gif"
     373              onclick="<%="toggleStacktrace(event, '" + ep.getId() + "')"%>"
     374              tooltip="Toggle display of detailed stacktrace"
     375              style="float: left;"
     376              tooltip="Error - click to show full stack trace"
     377            /><%=error.getMessage() %>
     378            <div id="stacktrace.<%=ep.getId()%>"
     379              class="stacktrace"
     380              style="display: none;"
     381              ><%=ThrowableUtil.stackTraceToString(error)%></div>
     382          </div>
     383          <%
    342384      }
    343385      %>
  • trunk/www/admin/extensions/tree.jsp

    r4510 r4618  
    4949  String icon = ec.isEnabled(ep) ? "ExtensionPoint" : "ExtensionPointDisabled";
    5050  ExtensionsFile f = ec.getFileByExtensionId(id);
    51   if (f == null || f.hasError()) icon = "ExtensionPointError";
     51  if (f == null || f.hasError() || ec.getLastExtensionPointError(id) != null)
     52  {
     53    icon = "ExtensionPointError";
     54  }
    5255  String joust = "ep = JoustMenu.addChildItem(" + parentNode +", '" + icon + "', " +
    5356      "'" + HTML.javaScriptEncode(name) + "', 'extensionPointOnClick(\"" + id + "\")', " +
     
    6265  String icon = ec.isEnabled(ext) ? "Extension" : "ExtensionDisabled";
    6366  ExtensionsFile f = ec.getFileByExtensionId(id);
    64   if (f == null || f.hasError()) icon = "ExtensionError";
     67  if (f == null || f.hasError() || ec.getLastExtensionError(id) != null)
     68  {
     69    icon = "ExtensionError";
     70  }
    6571  String joust = "ext = JoustMenu.addChildItem(" + parentNode + ", '" + icon + "', " +
    6672      "'" + HTML.javaScriptEncode(name) + "', 'extensionOnClick(\"" + id + "\")', " +
  • trunk/www/admin/services/services.jsp

    r4320 r4618  
    44  import="net.sf.basedb.core.DbControl"
    55  import="net.sf.basedb.core.Permission"
     6  import="net.sf.basedb.util.error.ThrowableUtil"
    67  import="net.sf.basedb.util.extensions.ActionIterator"
    78  import="net.sf.basedb.util.extensions.Extension"
    89  import="net.sf.basedb.util.extensions.ExtensionsFilter"
    910  import="net.sf.basedb.util.extensions.ExtensionsInvoker"
     11  import="net.sf.basedb.util.extensions.Registry"
    1012  import="net.sf.basedb.clients.web.Base"
    1113  import="net.sf.basedb.clients.web.extensions.ExtensionsControl"
     
    4648  {
    4749    location.href = 'index.jsp?ID=<%=ID%>&cmd=Start&extensionId=' + extensionId;
     50  }
     51  function toggleStacktrace(evt, extensionId)
     52  {
     53    Main.showHide('stacktrace.' + extensionId);
    4854  }
    4955  </script>
     
    8692          last = ext;
    8793          boolean isRunning = action.isRunning();
     94          Throwable error = ec.getLastExtensionError(ext.getId());
     95          boolean hasError = error != null;
    8896          %>
    8997          <tbl:row>
     
    99107              </tbl:cell>
    100108            <tbl:cell column="service"><%=action.toString()%></tbl:cell>
    101             <tbl:cell column="status"><%=isRunning ? "Running" : "Stopped"%></tbl:cell>
     109            <tbl:cell column="status">
     110              <%
     111              if (hasError)
     112              {
     113                %>
     114                <div class="error">
     115                <base:icon image="error.gif"
     116                    onclick="<%="toggleStacktrace(event, '" + ext.getId() + "')"%>"
     117                    tooltip="Toggle display of detailed stacktrace"
     118                    style="float: left;"
     119                    tooltip="Error - click to show full stack trace"
     120                /><%=error.getMessage() %>
     121                <div id="stacktrace.<%=ext.getId()%>"
     122                    class="stacktrace"
     123                    style="display: none;"
     124                  ><%=ThrowableUtil.stackTraceToString(error)%></div>
     125                </div>
     126                <%
     127              }
     128              else
     129              {
     130                %>
     131                <%=isRunning ? "Running" : "Stopped"%>
     132                <%
     133              }
     134              %>
     135            </tbl:cell>
    102136            <tbl:cell column="actions">
    103137              <%
  • trunk/www/include/styles/main.css

    r4510 r4618  
    228228}
    229229
     230.error .stacktrace {
     231  white-space: pre;
     232  font-family: monospace;
     233  text-align: left;
     234  font-size: smaller;
     235  background: #ffffd8;
     236  color: #000000;
     237  padding: 2px;
     238}
     239
    230240.helpmessage {
    231241  background: #E0E0E0;
Note: See TracChangeset for help on using the changeset viewer.