How to handle exceptions thrown in Wicket custom model?

折月煮酒 提交于 2019-12-10 17:32:51

问题


I have a component with a custom model (extending the wicket standard Model class). My model loads the data from a database/web service when Wicket calls getObject().

This lookup can fail for several reasons. I'd like to handle this error by displaying a nice message on the web page with the component. What is the best way to do that?

public class MyCustomModel extends Model {

    @Override
    public String getObject() {
        try {
            return Order.lookupOrderDataFromRemoteService();
        } catch (Exception e) {
            logger.error("Failed silently...");
            // How do I propagate this to the component/page?
        }           
        return null;
}

Note that the error happens inside the Model which is decoupled from the components.


回答1:


Your model could implement IComponentAssignedModel, thus being able to get hold on the owning component.

But I wonder how often are you able to reuse MyCustomModel? I know that some devs advocate creating standalone model implementations (often in separate packages). While there are general cases where this is useful (e.g. FeedbackMessagesModel), in my experience its easier to just create inner classes which are component specific.




回答2:


Handling an exception that happens in the model's getObject() is tricky, since by this time we are usually deep in the response phase of the whole request cycle, and it is too late to change the component hierarchy. So the only place to handle the exception is very much non-local, not anywhere near your component or model, but in the RequestCycle.

There is a way around that though. We use a combination of a Behavior and an IRequestCycleListener to deal with this:

  • IRequestCycleListener#onException allows you to examine any exception that was thrown during the request. If you return an IRequestHandler from this method, that handler will be run and rendered instead of whatever else was going on beforehand.

    We use this on its own to catch generic stuff like Hibernate's StaleObjectException to redirect the user to a generic "someone else modified your object" page. If you

  • For more specific cases we add a RuntimeExceptionHandler behavior:

    public abstract class RuntimeExceptionHandler extends Behavior {
        public abstract IRequestHandler handleRuntimeException(Component component, Exception ex);
    }
    

    In IRequestCycleListener we walk through the current page's component tree to see whether any component has an instance of RuntimeExceptionHandler. If we find one, we call its handleRuntimeException method, and if it returns an IRequestHandler that's the one we will use. This way you can have the actual handling of the error local to your page.

    Example:

    public MyPage() {
      ...
      this.add(new RuntimeExceptionHandler() {
        @Override public IRequestHandler handleRuntimeException(Component component, Exception ex) {
          if (ex instanceof MySpecialException) {
            // just an example, you really can do anything you want here.
            // show a feedback message...
            MyPage.this.error("something went wrong"); 
            // then hide the affected component(s) so the error doesn't happen again...
            myComponentWithErrorInModel.setVisible(false); // ...
            // ...then finally just re-render this page:
            return new RenderPageRequestHandler(new PageProvider(MyPage.this));
          } else {
            return null;
          }
        }
      });
    }
    

    Note: This is not something shipped with Wicket, we rolled our own. We simply combined the IRequestCycleListener and Behavior features of Wicket to come up with this.




回答3:


Being the main issue here that Models are by design decoupled from the component hierarchy, you could implement a component-aware Model that will report all errors against a specific component.

Remember to make sure it implements Detachable so that the related Component will be detached.

If the Model will perform an expensive operation, you might be interested in using LoadableDetachableModel instead (take into account that Model.getObject() might be called multiple times).

public class MyComponentAwareModel extends LoadableDetachableModel { 
    private Component comp;         
    public MyComponentAwareModel(Component comp) { 
          this.comp = comp;
    }

    protected Object load() {
        try {
            return Order.lookupOrderDataFromRemoteService();
        } catch (Exception e) {
            logger.error("Failed silently...");
            comp.error("This is an error message");
        }           
        return null;
    } 

    protected void onDetach(){
        comp.detach();
    }
}

It might also be worth to take a try at Session.get().error()) instead.




回答4:


I would add a FeedbackPanel to the page and call error("some description") in the catch clause.




回答5:


You might want to simply return null in getObject, and add logic to the controller class to display a message if getObject returns null.

If you need custom messages for different fail reasons, you could add a property like String errorMessage; to the model which is set when catching the Exception in getObject - so your controller class can do something like this

if(model.getObject == null) {
 add(new Label("label",model.getErrorMessage()));
} else {
 /* display your model object*/
}


来源:https://stackoverflow.com/questions/12456302/how-to-handle-exceptions-thrown-in-wicket-custom-model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!