How to handle exceptions thrown in DataProvider methods centrally - vaadin

When a DataProvider fetch or count method throws an exception, e.g. because the user is not authorized, how could I handle these exceptions centrally? I know there is HasErrorParameter interface to show error views when there is an exception thrown when routing. But these error views are not triggered when DataProvider throws the exception.
Example:
new AbstractBackEndDataProvider<String, Void>() {
#Override
protected Stream<String> fetchFromBackEnd(Query<String, Void> query) {
...
}
#Override
protected int sizeInBackEnd(Query<String, Void> query) {
throw new UnsupportedOperationException("test");
}
}
#Route("failed")
public class FailView extends VerticalLayout
implements HasErrorParameter<UnsupportedOperationException> {...}
Even if I do a try catch within the DataProvider methods, I don't see how I could navigate to the appropriate error view just by using the caught exception and not the view component class (this wouldn't trigger setErrorParameter method).
BTW: I miss the router exception handling topic in Vaadin Flow 13 documentation. I wonder why they removed it.

I believe all Exceptions that don't occur while routing will be given to the ErrorHandler of the VaadinSession the error occured in.
The best way to set the ErrorHandler seems to be to override the sessionInit method in a custom SessionInitListener
You can add a custom SessionInitListener inside the servletInitialized method of a custom VaadinServlet.
class CustomServlet extends VaadinServlet{
#Override
protected void servletInitialized() throws ServletException {
super.servletInitialized();
getService().addSessionInitListener(new CustomSessionInitListener());
}
}
And that SessionInitListener (in this example CustomSessionInitListener) has to set the errorHandler of the sessions that get initialized.
class CustomSessionInitListener implements SessionInitListener{
#Override
public void sessionInit(SessionInitEvent event) throws ServiceException {
event.getSession().setErrorHandler(new CustomErrorHandler());
}
}
For further information on how to create your own Servlet take a look at Vaadin's tutorial page(you need to scroll down to "Customizing Vaadin Servlet")
Edit:
To show the error page you need to get Vaadin to reroute to an error. To achieve that we can use an BeforeEnterEvent, BeforeEnterEvents have a rerouteToError method which we can use to let Vaadin show our ErrorView.
But we also want to pass along the Exception instance, so we have to store that as well. I did exactly that with the following class:
#Route("error-view") // Route shown in the user's browser
public class ErrorViewShower extends Div implements BeforeEnterObserver {
// Class to store the current Exception of each UI in
private static class UIExceptionContainer extends HashMap<UI, Exception> {
}
// Method to call when we want to show an error
public static void showError(Exception exception) {
UIExceptionContainer exceptionContainer = VaadinSession.getCurrent().getAttribute(UIExceptionContainer.class);
// Creating and setting the exceptionContainer in case it hasn't been set yet.
if (exceptionContainer == null) {
exceptionContainer = new UIExceptionContainer();
VaadinSession.getCurrent().setAttribute(UIExceptionContainer.class, exceptionContainer);
}
// Storing the exception for the beforeEnter method
exceptionContainer.put(UI.getCurrent(), exception);
// Now we navigate to an Instance of this class, to use the BeforeEnterEvent to reroute to the actual error view
UI.getCurrent().navigate(ErrorViewShower.class);// If this call doesn't work you might want to wrap into UI.access
}
#Override
public void beforeEnter(BeforeEnterEvent event) {
UIExceptionContainer exceptionContainer = VaadinSession.getCurrent().getAttribute(UIExceptionContainer.class);
// Retrieving the previously stored exception. You might want to handle if this has been called without setting any Exception.
Exception exception = exceptionContainer.get(UI.getCurrent());
//Clearing out the now handled Exception
exceptionContainer.remove(UI.getCurrent());
// Using the BeforeEnterEvent to show the error
event.rerouteToError(exception, "Possible custom message for the ErrorHandler here");
}
}
Usage of it in combination with the error handler looks like this:
public class CustomErrorHandler implements ErrorHandler {
#Override
public void error(ErrorEvent event) {
// This can easily throw an exception itself, you need to add additional checking before casting.
// And it's possible that this method is called outside the context of an UI(when a dynamic resource throws an exception for example)
Exception exception = (Exception) event.getThrowable();
ErrorViewShower.showError(exception);
}
}
Edit2:
As it turns out that Exceptions occuring inside internal method calls don't get handled by the UI's ErrorHandler or the VaadinSession's ErrorHandler but instead by another error handler which causes the client side to terminate and show the Error Notification,
a solution is to catch the Exceptions inside the methods of the DataProvider and pass them to ErrorViewShower.showError() and still return without any Exception flying the stacktrace upwards. (Or don't throw any Exception yourself and instead simply pass a new to the ErrorViewShower.showError() method).
By returning normally Vaadin doesn't even know something went wrong.
ErrorViewShower.showError() calls ui.navigate, that navigation command seems to get "queued" behind the calls to the DataProvider, meaning the view of the user will change in the same request.
Dataprovider with such an implementation:
new AbstractBackEndDataProvider<String, Void>() {
#Override
protected Stream<String> fetchFromBackEnd(Query<String, Void> query) {
try{
//Code that can throw an Exception here
}catch(Exception e){
ErrorViewShower.showError(e);
//We have to make sure that query.getLimit and query.getOffset gets called, otherwise Vaadin throws an Exception with the message "the data provider hasn't ever called getLimit() method on the provided query. It means that the the data provider breaks the contract and the returned stream contains unxpected data."
query.getLimit();
query.getOffset();
return Stream.of(); //Stream of empty Array to return without error
}
}
#Override
protected int sizeInBackEnd(Query<String, Void> query) {
//Second way i mentioned, but this will not catch any Exception you didn't create, where as the try...catch has no way to let any Exception reach Vaadin.
if(badThingsHappened){
ErrorViewShower.showError(new UnsupportedOperationException("Bad things..."));
return 0;//Exiting without error
}
}
}

Related

Simple Injector throws an error in Caliburn.Micro Bootstrapper.Buildup when calling async method in the viewmodel

I am trying to use Simple Injector as the DI container for Caliburn.Micro. Demo source: https://github.com/nvstrien/WPFDemos
This project has a basic Caliburn.Micro setup with a Simple Injector Container. The ShellView has 1 button and when pressed, an async method is called to get some simulated data.
I am getting this error in Bootstrapper.Buildup.
SimpleInjector.ActivationException: 'No registration for type SequentialResult could be found. Make sure SequentialResult is registered, for instance by calling 'Container.Register<SequentialResult>();' during the registration phase. An implicit registration could not be made because Container.Options.ResolveUnregisteredConcreteTypes is set to 'false', which is now the default setting in v5. This disallows the container to construct this unregistered concrete type. For more information on why resolving unregistered concrete types is now disallowed by default, and what possible fixes you can apply, see https://simpleinjector.org/ructd. '
It has been suggested here that commenting out Bootstrapper.BuildUp should work: Caliburn.Micro Bootstrapper 'BuildUp' method throws exception when Simple Injector is used
However, when doing so, SimpleInjector will still throw an exception.
Any help solving this would be greatly appreciated
My complete Bootstrapper config file looks like this:
public static readonly Container _container = new();
public Bootstrapper()
{
Initialize();
}
protected override void Configure()
{
_container.Register<IWindowManager, WindowManager>();
_container.RegisterSingleton<IEventAggregator, EventAggregator>();
GetType().Assembly.GetTypes()
.Where(type => type.IsClass)
.Where(type => type.Name.EndsWith("ViewModel"))
.ToList()
.ForEach(viewModelType => _container.RegisterSingleton(viewModelType, viewModelType));
_container.Verify();
}
protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
{
DisplayRootViewFor<ShellViewModel>();
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
// as discussed here: https://stackoverflow.com/questions/32258863/simple-injector-getallinstances-throwing-exception-with-caliburn-micro
//_container.GetAllInstances(service);
IServiceProvider provider = _container;
Type collectionType = typeof(IEnumerable<>).MakeGenericType(service);
IEnumerable<object> services = (IEnumerable<object>)provider.GetService(collectionType);
return services ?? Enumerable.Empty<object>();
}
protected override object GetInstance(System.Type service, string key)
{
return _container.GetInstance(service);
}
protected override IEnumerable<Assembly> SelectAssemblies()
{
return new[] { Assembly.GetExecutingAssembly() };
}
// see: https://stackoverflow.com/questions/37631468/caliburn-micro-bootstrapper-buildup-method-throws-exception-when-simple-inject
// commenting out BuildUp still throws an exception in SimpleInjector.dll
protected override void BuildUp(object instance)
{
InstanceProducer registration = _container.GetRegistration(instance.GetType(), true);
registration.Registration.InitializeInstance(instance);
}
In the ShellViewModel I have 1 method that runs when pressing a button on the ShellView.
public async Task Button1()
{
Debug.Print("Hello world");
var data = await GetSampleDataAsync();
foreach (var item in data)
{
Debug.Print(item);
}
}
public async Task<IEnumerable<string>> GetSampleDataAsync()
{
// method simulating getting async data
var data = new List<string>() { "hello", "world" };
return await Task.FromResult(data);
}
The error occurs when 'await GetSampleDataAsync()' gets called.
When adding the SequentialResult in Bootstrapper.Configure as follows.
protected override void Configure()
{
_container.Register<IWindowManager, WindowManager>();
_container.RegisterSingleton<IEventAggregator, EventAggregator>();
_container.Register<SequentialResult>();
GetType().Assembly.GetTypes()
.Where(type => type.IsClass)
.Where(type => type.Name.EndsWith("ViewModel"))
.ToList()
.ForEach(viewModelType => _container.RegisterSingleton(viewModelType, viewModelType));
_container.Verify();
}
I get the next error:
System.InvalidOperationException
HResult=0x80131509 Message=The configuration is invalid. Creating
the instance for type SequentialResult failed. The constructor of type
SequentialResult contains the parameter with name 'enumerator' and
type IEnumerator<IResult>, but IEnumerator<IResult> is not registered.
For IEnumerator<IResult> to be resolved, it must be registered in the
container. Source=SimpleInjector StackTrace: at
SimpleInjector.InstanceProducer.VerifyExpressionBuilding() at
SimpleInjector.Container.VerifyThatAllExpressionsCanBeBuilt(InstanceProducer[]
producersToVerify) at
SimpleInjector.Container.VerifyThatAllExpressionsCanBeBuilt() at
SimpleInjector.Container.VerifyInternal(Boolean
suppressLifestyleMismatchVerification) at
SimpleInjector.Container.Verify(VerificationOption option) at
SimpleInjector.Container.Verify() at
CaliburnMicroWithSimpleInjectorDemo.Bootstrapper.Configure() in
C:\Users\Niels\source\repos\WPFDemos\CaliburnMicroWithSimpleInjectorDemo\Bootstrapper.cs:line
37 at Caliburn.Micro.BootstrapperBase.StartRuntime() at
Caliburn.Micro.BootstrapperBase.Initialize() at
CaliburnMicroWithSimpleInjectorDemo.Bootstrapper..ctor() in
C:\Users\Niels\source\repos\WPFDemos\CaliburnMicroWithSimpleInjectorDemo\Bootstrapper.cs:line
22
This exception was originally thrown at this call stack:
[External Code]
Inner Exception 1: ActivationException: The constructor of type
SequentialResult contains the parameter with name 'enumerator' and
type IEnumerator<IResult>, but IEnumerator<IResult> is not registered.
For IEnumerator<IResult> to be resolved, it must be registered in the
container.
When I change the methods in my ShellViewModel to be synchronous like this, I don't get any exceptions:
public void Button1()
{
Debug.Print("Hello world");
var data = GetSampleData();
foreach (var item in data)
{
Debug.Print(item);
}
}
public IEnumerable<string> GetSampleData()
{
var data = new List<string>() { "hello", "world" };
return data;
}
It seems to me that the container doesn't get configured properly to work with some implementation in Caliburn.Micro, but the Bootstrapper configuration follows the recommended path. I am unfortunately not able to follow the explanation here: Caliburn.Micro Bootstrapper 'BuildUp' method throws exception when Simple Injector is used Also, what was marked as solution doesn't seem to work in my code sample.
#Steven: Commenting out BuildUp indeed fixed my problem.
I thought that I had tested commenting out BuildUp in my sample project before coming to SO to ask my question, but trying it again now solved my problem. Thank you for putting me back on the right track!
Solution: comment out / delete BootStrapper.BuildUp as was also suggested here: Caliburn.Micro Bootstrapper 'BuildUp' method throws exception when Simple Injector is used
//protected override void BuildUp(object instance)
//{
// InstanceProducer registration = _container.GetRegistration(instance.GetType(), true);
// registration.Registration.InitializeInstance(instance);
//}

How to navigate to another vaadin class UI using thread

I created an asynchronous thread to navigate from one UI class to another UI class after 30 seconds by showing a timer(H1 tag) to the user. Thread successfully shows updates on H1 tag but does not navigate to the next UI class after the end of 30 seconds. I'm getting an error Exception call "java.lang.IllegalStateException: Cannot access state in VaadinSession or UI without locking the session." for ui.navigate(ScoreBoard.class); call.
#Override
protected void onAttach(AttachEvent attachEvent) {
// Start the data feed thread
thread = new FeederThread(attachEvent.getUI(),timerc);
thread.start();
}
//Thread
private static class FeederThread extends Thread {
private final com.vaadin.flow.component.UI ui;
private final H1 element;
private int count = 30;
public FeederThread(com.vaadin.flow.component.UI ui,H1 element) {
this.ui = ui;
this.element = element;
}
#Override
public void run() {
while (count>-1){
try {
Thread.sleep(1000);
ui.access(()-> {
element.setText(String.valueOf(count)+" sec");
});
count--;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//Exception in thread "Thread-46" java.lang.IllegalStateException: //Cannot access state in VaadinSession or UI without locking the session.
ui.navigate(ScoreBoard.class);
}
}
//Exception in thread "Thread-46" java.lang.IllegalStateException: //Cannot access state in VaadinSession or UI without locking the session.
ui.navigate(ScoreBoard.class);
UI.getCurrent() is returning null when called in Thread, this is intentional. This way it can be ensured that no wrong UI is returned.
The correct pattern would be for example add a method in your view, which updates the Text. In the method you can use getUi().ifPresent(ui -> ui.access(..)) . Then you can call that method from the Thread safely. Same can be applied with navigation.
Alternatively you can pass ui as parameter to your Thread as you have done. When you do so, getCurrent() call is obsolote.
You need to enable push by using #Push in your class. Also, since the navigation action is part of the UI state, you need to use UI.access. Finally, you don't need to call getCurrent() if you already have the instance. So this is what you need in short:
...
#Push
public class MainView extends VerticalLayout {
...
ui.access(() -> ui.navigate(ScoreBoard.class));
...
}

How to log custom exception with additional Data property to Elmah?

I have a custom exception, where I have overriden the Data property using reflection like the following...
public class MyCustomException : Exception
{
private readonly SomeModel _log;
public MyCustomException(SomeModel log)
: base(string.Format("Could not insert to some table"))
{
_log = log;
}
public override System.Collections.IDictionary Data
{
get
{
var data = new Dictionary<string, object>();
foreach (PropertyInfo pinfo in _log.GetType().GetProperties())
{
data.Add(pinfo.Name, pinfo.GetType().GetProperty(pinfo.Name));
}
return data;
}
}
}
When the above exception is thrown, it gets logged to elmah but the Data is not logged.
What changes do I have to make so that the Data is also logged to elmah ? Please advice.
The Detail property of the Elmah.Error object - which is then processed by an ErrorLog class - is built from the ToString() method of the exception.
// Elmah.Error
public Error(Exception e, HttpContext context)
{
// snip
this._detail = e.ToString(); // here
// snip
Add your data to an override of the ToString method in the MyCustomException to see it in Elmah.
Your question is currently the issue with most stars on the ELMAH issue tracker:
https://code.google.com/p/elmah/issues/detail?id=162
#samy may be more correct, but I have also found another possible option that works for my situation. I am using elmah in a webapi2 project where users are anonymous and in one particular controller I want to record some context of the request from the viewmodel (in my case, an email address but I could potentially record more data) and I want to be able to associate errors to email so I can determine, after an error, if the same user was able to submit an order successfully.
In my controller, I perform a number of database calls in a transaction and then submit an order to paypal, all within a try/catch block. In the catch, I create a new exception instance with a message containing the email and set the innerException property to the thrown exception and throw the new exception.
I know it is possible to lose some stack trace information, I tested this in my context and the stack trace seems to be maintained but exceptions occur inside the controller because there are not many layers to this particular controller and application. If anyone has a similar situation, this method might be the quickest and easiest.
catch (Exception ex)
{
Exception newException = new Exception(viewModel.ContactEmail, ex);
throw newException;
}
This assumes you have a exception filter, such as below (for webapi), and the filter is registered as global in global.asax.
public class LogExceptionAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
if (HttpContext.Current != null)
{
ErrorSignal.FromCurrentContext().Raise(actionExecutedContext.Exception);
}
}
}

Silence FullAjaxExceptionHandler

So after being confronted with the dreaded javax.faces.application.ViewExpiredException, I had to go look around the internet to find the proper solution. Fortunately, the solutions are readily available and I went ahead and adopted the OmniFaces FullAjaxExceptionHandler.
Enough said, as with pretty much everything from OmniFaces, it worked wonders. But, every time I have a view expiring I am getting :
SEVERE: WebModule[/myModule]FullAjaxExceptionHandler: An exception occurred during processing JSF ajax request. Error page '/WEB-INF/errorpages/test.xhtml' will be shown.
javax.faces.application.ViewExpiredException: viewId:/my/page.xhtml - View /my/page.xhtml could not be restored.
...
This is fine as it is handled as expected, but is there anyway to silence this exception from being printed to the server.log? This would crowd the log pretty quickly.
I am running :
Mojarra 2.1.23
PrimeFaces 4.0-SNAPSHOT
OmniFaces 1.6-SNAPSHOT-2013-07-01
on
Glassfish 3.1.2.2
As per OmniFaces 1.6, you can extend it and override the method logException() as below to skip the stack trace for ViewExpiredException.
public class YourAjaxExceptionHandler extends FullAjaxExceptionHandler {
public YourAjaxExceptionHandler(ExceptionHandler wrapped) {
super(wrapped);
}
#Override
protected void logException(FacesContext context, Throwable exception, String location, String message, Object... parameters) {
if (exception instanceof ViewExpiredException) {
// With exception==null, no trace will be logged.
super.logException(context, null, location, message, parameters);
}
else {
super.logException(context, exception, location, message, parameters);
}
}
}
Create a factory around it:
public class YourAjaxExceptionHandlerFactory extends ExceptionHandlerFactory {
private ExceptionHandlerFactory wrapped;
public YourAjaxExceptionHandlerFactory(ExceptionHandlerFactory wrapped) {
this.wrapped = wrapped;
}
#Override
public ExceptionHandler getExceptionHandler() {
return new YourAjaxExceptionHandler(getWrapped().getExceptionHandler());
}
#Override
public ExceptionHandlerFactory getWrapped() {
return wrapped;
}
}
In order to get this to run, register it as factory in faces-config.xml the usual way (don't forget to remove the original registration for FullAjaxExceptionHandlerFactory):
<factory>
<exception-handler-factory>com.example.YourExceptionHandlerFactory</exception-handler-factory>
</factory>

Custom Exception Filter not being hit in asp.net MVC

I have a custom exception filter that I'm using to catch a custom exception that I wrote but for some reason when I throw my exception, it's not ever getting to the filter. Instead I just get an error that my exception was not handled by user code. Can anyone please provide some advice/assistance as to how I should have this set up? Relevant code is below:
// controller
[CustomExceptionFilter]
public class SomeController : Controller
{
public SomeController()
{
}
public ActionResult Index()
{
SomeClass.SomeStaticMethod();
return View();
}
}
that's the controller with the customexception attribute
// some class (where exception is being thrown)
public class SomeClass
{
public static void SomeStaticMethod()
{
throw new MyCustomException("Test");
}
}
that's the class (for my test) that throws the exception (I've also tried throwing it directly on the controller).
// Custom exception filter (want this to catch all unhandled exceptions)
public class CustomExceptionFilter : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (filterContext.Exception.GetType() == typeof(MyCustomException))
{
// do stuff
}
}
}
that's the custom exception filter...it's never being reached when the code is executed and the exception is thrown. Instead I get the error mentioned above. Everything I've read indicates that this is the proper way to set this up, but when I put breakpoints in my custom filter, it's never being hit....
What am I missing here?
TIA
Once you've handled your error, you need to let the filter context know it has been handled. Like this:
filterContext.ExceptionHandled = true;
This should be in your '// do stuff' section.
I've copied your code, and the filter is getting called fine. The only difference I made was I added the exceptionHandled code and adding my breakpoint at that line.

Resources