I have a requirement to create custom exception for the exceptions generated by my app/module.
I want to consolidate all the exception classes in one place and handle the exceptions in one place.
I might have generic exceptions, like mentioned below, which I would like in one common place
input invalid
internal error (database errors, smtp errors, other failures)
permission denied
session error
I might have specific exceptions, like mentioned below
email not valid, etc.
Specific exceptions might be a subclass of generic exceptions in cases, like "email not valid" could fall under "input invalid" exception.
I Should be able to send data along with the exception message while throwing. (Data will be in arrays or objects if feasible)
Whats the best way to go about it?
What is the best way to organize custom exceptions?
How to code in such a way that we don't have to catch common exceptions every where but at the same time user gets a meaningful error.
After calling a method we should only catch specific exceptions that the method can throw.
I would suggest you to move to Kohana 3.2 as there is a change in the way Kohana handles exception in that new stable version. Assuming you are going to use v3.2, this is how you could manage custom exceptions:
First of all, you need to modify bootstrap.php and make sure 'errors' is to true in the Kohana::init() call. This will make sure that Koahana will handle all unhandled exceptions thrown by you or the system. if you check \classes\kohana\core.php, Kohana registers its exception handler class Kohana_Exception using php call below
set_exception_handler(array('Kohana_Exception', 'handler'));
The default exception handler does a nice job of handling all types of Exceptions and writing the message to the log folder and displaying a basic error page. If you look inside Kohana_Exception, it is a subclass of Kohana_Kohana_Exception class, which is where the logic is written.
Now, to customize things:
If you are looking for just showing a custom page for showing your errors, just create a view named application/views/kohana/error.php and put your custom error page there. it will override the system's default error view file found at system/views/kohana/error.php.
If you are looking for changing the way you log the error or do some custom processing based on specific type of errors, you need to override Kohana_Exception class or register your own derived exception handler by calling set_exception_handler() at the end of bootstrap.php.
To override Kohana_Exception, just copy paste /system/classes/kohana/exception.php to application/classes/kohana/exception.php and override the handler() and/or text() method. for e.g. below I am custom handling 404 error and also including user_id to error log for debugging.
:
class Kohana_Exception extends Kohana_Kohana_Exception
{
/**
* Overriden to show custom page for 404 errors
*/
public static function handler(Exception $e)
{
switch (get_class($e))
{
case 'HTTP_Exception_404':
$response = new Response;
$response->status(404);
$view = new View('error/report_404');
$view->message = $e->getMessage();
echo $response->body($view)->send_headers()->body();
if (is_object(Kohana::$log))
{
// Add this exception to the log
Kohana::$log->add(Log::ERROR, $e);
// Make sure the logs are written
Kohana::$log->write();
}
return TRUE;
break;
default:
return Kohana_Kohana_Exception::handler($e);
break;
}
}
/**
* Override if necessary. E.g. below include logged in user's info in the log
*/
public static function text(Exception $e)
{
$id = <get user id from session>;
return sprintf('[user: %s] %s [ %s ]: %s ~ %s [ %d ]',
$id, get_class($e), $e->getCode(), strip_tags($e->getMessage()), Debug::path($e->getFile()), $e->getLine());
}
}
Helpful external links and references:
http://kohana.sebicas.com/index.php/guide/kohana/errors
http://kohanaframework.org/3.1/guide/kohana/tutorials/error-pages
Related
I have an aggregator configured via the Java DSL in a Spring Integration flow, and I want to throw an exception that goes to the global error channel when a group timeout occurs.
The discard channel is no good for me because discard messages are at the group member level, rather than for the whole group.
I've tried using the application event publisher as follows, but the publisher object doesn't seem to get invoked:
.aggregate(new Consumer<AggregatorSpec>() {
#Override
public void accept(AggregatorSpec aggregatorSpec) {
try {
aggregatorSpec
.outputProcessor(groupPublishStrategy())
.correlationStrategy(groupPublishStrategy())
.releaseStrategy(groupPublishStrategy())
.groupTimeout(groupTimeout)
.get().getT2().setApplicationEventPublisher(myGroupExpirationPublisher());
}
catch (Exception e) {
e.printStackTrace();
}
}
})
Is there a recommended way to get notification for this use case? Any ideas why the above doesn't seem to work?
I suppose I could extend the AggregatorSpec class to get the message handler configured the way I want, but I wanted to see if I could do this with stock SI classes.
Have just tested and ApplicationEventPublisher is populated properly on the bean initialization phase.
The expireGroup(Object correlationKey, MessageGroup group) is enough big to demonstrate it here, but you can find its code on GitHub. So, MessageGroupExpiredEvent is always published when we reach this method.
Of course if discardMessage(message); doesn't throw exception.
OTOH the exprireGroup() is reachable only in this case:
if (this.releaseStrategy.canRelease(groupNow)) {
completeGroup(correlationKey, groupNow);
}
else {
expireGroup(correlationKey, groupNow);
}
So, please, be sure that your groupPublishStrategy() has proper logic and doesn't return true when your group isn't completed yet.
Well, it really would be better if you debug AbstractCorrelatingMessageHandler for your use-case. If you are sure that your group isn't completed during some groupTimeout, the forceComplete(MessageGroup group) is a good place for you to start debugging.
Otherwise, please, share DEBUG logs for the org.springframework.integration category, when you think that an event has to be emitted.
This is not a question about the RequestLogsService or the RequestLogFeature. It is about the ServiceRunner's call to a IRequestLogger (if one is registered at the app container).
My app has regular logging in place, I dump the app's flow to log a file.
I am now implementing a database log, inspired by the RequestLogsService. This log will contain one line per request, containing all the Request and Response data.
However my endpoint produces outputs at 4 different levels:
Custom auth filter
Validation
Service
AppHostExceptionHandler
From those 4, only the Service outputs are covered by the IRequestLogger, because its ServiceRunner related. Is there any way I can cover my 4 scenarios transparently? I want to minimize the complexity introduced in the pipeline.
Thanks
I encountered a similar problem recently and resolved it as follows:
Custom Auth filter
There are 2 possibilities with this one based upon the type of logging you would like. If you throw an exception in here, you can catch it by setting up an ServiceExceptionHandler in AppHost:
this.ServiceExceptionHandler = (httpRequest, request, exception) =>
{
LogData(httpRequest, exception);
return DtoUtils.HandleException(this, request, exception);
};
If that approach won't work for you or you don't throw an exception during auth, you will have to instead create a logging filter either before or after the auth filter is run. This can be done in a few different locations using either a PreRequestFilter or a RequestFilter depending on when exactly you need it to run (see ServiceStack's Order of Operations).
Validation
I'll assume you are using ServiceStack's built in FluentValidation to perform your validation. In this case, you can hook into the plugin's ErrorResponseFilter like so. Note that if you require the IHttpRequest and IHTTPresponse objects in this method, you may have to do a hack-y workaround and stash them somewhere as they aren't supplied.:
Plugins.Add(new ValidationFeature { ErrorResponseFilter = ValidationErrorResponseFilter});
...
private object ValidationError(ValidationResult validationResult, object o)
{
var httpError = o as HttpError;
LogData(httpError);
}
AppHostExceptionHandler
If the exception occurs in setup of the AppHost, you may have to settle with a simple try/catch. If the exception occurs in AppHost during the request processing however, you can use the ExceptionHandler similar to the ServiceExceptionHandler above:
this.ExceptionHandler = (httpReq, httpRes, operationName, ex) =>
{
LogData(httpReq, httpRes);
}
In conclusion, if all of this doesn't work or is too much code in too many places, you can instead resort to overriding one or more methods in ServiceRunner to get what you need. In my case I overrode OnAfterExecute to log every request that made it to the service handler and then only had to add the exception handler to FluentValidation as noted above.
I am learning ANTLR4 and I have no previous experience with parser generators.
When I define my own visitor implementation, I have to override methods of the BaseVisitor (I am looking for instance at the EvalVisitor class at page 40 of the book). In case my method implementation possibly throws an exception, what should I do? I cannot use a checked exception since the original method has an empty throws clause. Am I expected to use unchecked exceptions? (this seems a bad Java design). For instance, assume that in the EvalVisitor class I want method visitId (page 41) to throw a user-defined exception, say UndefinedId, rather than returning 0. How should I write my code?
You have two options:
Handle the exception inside the visitor method itself.
Wrap your checked exception in an unchecked exception. One possibility is ParseCancellationException, but you'll have to determine for yourself whether or not that makes sense in your application.
try {
...
} catch (IOException ex) {
throw new ParseCancellationException(ex);
}
In my preRenderView invoked method some validations are performed and if it fails a redirect shall happen.
But i am getting an IllegalStateException
Information: Exception when handling error trying to reset the response.
java.lang.IllegalStateException
at org.apache.catalina.connector.ResponseFacade.sendRedirect(ResponseFacade.java:524)
at com.sun.faces.context.ExternalContextImpl.redirect(ExternalContextImpl.java:602)
at package.FacesContextUtils.redirect(FacesContextUtils.java:581)
Here is the code:
public void initPreRenderView(final String value) throws DatabaseException
{
if (value == null)
{
FacesContextUtils.addMessageInvalidLinkRedirect(context, url);
return;
}
}
Basically the utility function consists of:
public static void addMessageInvalidLinkRedirect(FacesContext context, String url)
{
context.addMessage(null, new FacesMessage("Invalid link..."));
try
{
context.getExternalContext().redirect(url);
}
catch (final IOException e)
{
// add error message
}
}
Many answers regarding this topic suppose to add a return after the redirection statement, which I did in the preRenderView method.
Edit:
The redirection takes place and everything works as expected. Just want to get rid of this error message.
The problem is not the presence or absence of the return statement. The problem is that the response cannot be reset. Let's look where this is caused:
java.lang.IllegalStateException
at org.apache.catalina.connector.ResponseFacade.sendRedirect(ResponseFacade.java:524)
Based on source code this will be thrown when isCommitted() returns true. Thus, the response is already committed. A part of the response has already been sent to the client.
There's nothing in your question which indicates that. So, the cause of the problem has to be sought elsewhere than in the information provided so far. On standard JSF this should not happen, so perhaps you've somewhere a servlet filter which is setting/committing some headers?
I was getting a similar issue. The problem turned out being that another redirect request was being made before I invoked the one I expected. Because one redirect request had already been made it threw an IllegalStateException.
Say I have a User that I want to delete, but the User might be referenced by other tables in the database. Is there a way to check for references to this user before trying to delete or is the best option to just delete and catch/handle the exception that SaveChanges() throws?
Obviously I could check each table where the user might be referenced...but I would rather not as it is referenced in a few places and it just seems like a messy way to do things.
I don't now if you have found a solution to this yet but I'm posting since i run into a similar problem myself. I suppose you could use a query to check for references lets say something like..
bool related = db.Article.Where(i => i.CategoryId == id).Any();
But i believe it is better to catch the exception than to check for references.
For scenarios where you want a required relationship but no cascade delete you
can explicitly override the convention and configure cascade delete behavior with the
Fluent API.
The Fluent API method to use is called WillCascadeOnDelete and takes a Boolean as a
parameter. This configuration is applied to a relationship, which means that you first
need to specify the relationship using a Has/With pairing and then call WillCascadeOn
Delete. Something like:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Article>()
.HasRequired(a => a.Category)
.WithMany(i => i.Articles)
.WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
Then you usually get a DbUpdateException or a InvalidOperationException depending on how your data is loaded into memory. You can catch them with a simple statement and add a message to the user.
try
{
db.Category.Remove(category);
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DataException)
{
ModelState.AddModelError("", "Your message here");
return View(category);
}
What WillCascadeOnDelete basically does is that it changes the Delete rule in your database from Cascade to No Action which causes the error to be thrown when a violation occurs.
The overall message here is that you have control over the cascade delete setting, but
you will be responsible for avoiding or resolving possible conflicts caused by
not having a cascade delete present. It worked for me, hope it helps you too.
See also:Configuring Relationships with the Fluent API