I am looking to develop an error handling strategy for a SharePoint solution that makes use of sandboxed webparts. I was initially looking at a general exception handling approach based on this article, but this doesn't work for sandboxed webparts. Once an unhandled exception has been thrown in the sandbox, the user code service appears to take control, so that the exception handling in the base web part isn't reached. Are there any established error handling approaches for sandboxed solutions?
Is anyone aware of a method of determining when an unhandled exception has been thrown in a sandboxed webpart, if only to change the displayed error message to a more user friendly message? I would like to replace the standard "Web Part Error: Unhandled exception was thrown by the sandboxed code wrapper's Execute method in the partial trust app domain: An unexpected error has occurred." message at very least.
Thanks, MagicAndi.
Actually, you can follow the approach suggested by the article you mentioned. You just have to provide safe overridables for all virtual properties and methods your descendant web parts are going to override. The patter can be described:
Override and seal every virtual property and method supposed to be overriden with code that can throw an exception.
Create a virtual counterpart of the overridable with the same prototype and call the base class from it if necessary. This is supposed to be overriden by your descendants.
Call the new overridable from the sealed member in a try&catch and remember the exception if caught there.
Rendering method either renders the usual content or the remembered error message.
This is a torso of the base class I use:
public class ErrorSafeWebPart : WebPart {
#region Error remembering and rendering
public Exception Error { get; private set; }
// Can be used to skip some code later that needs not
// be performed if the web part renders just the error.
public bool HasFailed { get { return Error != null; } }
// Remembers just the first error; following errors are
// usually a consequence of the first one.
public void RememberError(Exception error) {
if (Error != null)
Error = error;
}
// You can do much better error rendering than this code...
protected virtual void RenderError(HtmlTextWriter writer) {
writer.WriteEncodedText(Error.ToString());
}
#endregion
#region Overriddables guarded against unhandled exceptions
// Descendant classes are supposed to override the new DoXxx
// methods instead of the original overridables They should
// not catch exceptions and leave it on this class.
protected override sealed void CreateChildControls() {
if (!HasFailed)
try {
DoCreateChildControls();
} catch (Exception exception) {
RememberError(exception);
}
}
protected virtual void DoCreateChildControls()
{}
protected override sealed void OnInit(EventArgs e) {
if (!HasFailed)
try {
DoOnInit(e);
} catch (Exception exception) {
RememberError(exception);
}
}
protected virtual void DoOnInit(EventArgs e) {
base.OnInit(e);
}
// Continue similarly with OnInit, OnLoad, OnPreRender, OnUnload
// and/or others that are usually overridden and should be guarded.
protected override sealed void RenderContents(HtmlTextWriter writer) {
// Try to render the normal contents if there was no error.
if (!HasFailed)
try {
DoRenderContents(writer);
} catch (Exception exception) {
RememberError(exception);
}
// If an error occurred in any phase render it now.
if (HasFailed)
RenderError(writer);
}
protected virtual void DoRenderContents(HtmlTextWriter writer) {
base.RenderContents(writer);
}
#endregion
}
--- Ferda
Related
Hello Liferay Experts,
I have a requirement where I need to stop an Admin from assigning a role, I am trying to implement this with a ModelListener.
Here is the code..
#Component(immediate = true, service = ModelListener.class)
public class TestUserModelListener extends BaseModelListener<User> {
#Override
public void onBeforeAddAssociation(Object classPK, String associationClassName, Objext accociationClassPK) throws ModelListenerException {
// ...
throw new ModelListenerException("User creation not allowed");
}
}
When this code executes, the exception is thrown but the UI doesnt handle it correctly, the control panel Menus are not displayed and the exception message is not displayed to the user.
How to throw an exception and handle it correctly in UI and display error message to the user.
Thanks
M
Andre Albert already gave you the correct hints in the comments.
You should keep the ModelListener and override the ActionCommand additionally.
First, read the tutorial about Overriding MVC Comands. When implementing your custom Command, use Liferay's implemenation as basis (don't forget to add the higher service.ranking) and replace the catch block with something like this:
// I took the freedom and refactored Liferay's catch block a little bit
catch (NoSuchUserException | PrincipalException e) {
SessionErrors.add(actionRequest, e.getClass());
actionResponse.setRenderParameter("mvcPath", "/error.jsp");
} catch (MembershipPolicyException e) {
SessionErrors.add(actionRequest, e.getClass(), e);
actionResponse.setRenderParameter("mvcPath", "/edit_user.jsp");
actionResponse.setRenderParameter("screenNavigationCategoryKey", UserFormConstants.CATEGORY_KEY_GENERAL);
actionResponse.setRenderParameter("screenNavigationEntryKey", UserFormConstants.ENTRY_KEY_ROLES);
} catch (ForbiddenRoleAssociationException e) {
// Here you can add a SessionError
// and set some render parameters
} catch (Exception e) {
throw e;
}
The ForbiddenRoleAssociationException does not exist yet. It's purpose is to distinguish this special case of a ModelListenerException from others which might not interest you. You'll have to implement it yourself. Just extend the ModelListenerException:
public class ForbiddenRoleAssociationException extends ModelListenerException {
// here might be some constructors
}
Now adjust your ModelListener so that it throws your new ForbiddenRoeAssociationException:
#Component(immediate = true, service = ModelListener.class)
public class TestUserModelListener extends BaseModelListener<User> {
#Override
public void onBeforeAddAssociation(Object classPK, String associationClassName, Objext accociationClassPK) throws ModelListenerException {
// ...
throw new ForbiddenRoleAssociationException(); // or any other constructor
}
}
This way you should be able to display error messages to admins (depending on your code in the catch block of the ForbiddenRoleAssociationException) and circumvent any other (programmatic) attempt to assign the Role as well.
I'm uising web api 2 to develop services for a client, to manage errors we are using a ExceptionsFilterAttribute, but as you know, in this level not all exception are catched.
Some errors are raised in protected void Application_AuthenticateRequest(Object sender, EventArgs e) and I want to handle them and send a custom message to our client to give him more details about the error, to solve this I create a GlobalExceptionHandler
public class GlobalExceptionHandler: ExceptionHandler
{
//A basic DTO to return back to the caller with data about the error
private class ErrorInformation
{
public string Message { get; set; }
public DateTime ErrorDate { get; set; }
}
public override void Handle(ExceptionHandlerContext context)
{
//Return a DTO representing what happened
context.Result = new ResponseMessageResult(context.Request.CreateResponse(HttpStatusCode.InternalServerError,
new ErrorInformation { Message="We apologize but an unexpected error occured. Please try again later.", ErrorDate=DateTime.UtcNow }));
//This is commented out, but could also serve the purpose if you wanted to only return some text directly, rather than JSON that the front end will bind to.
//context.Result = new ResponseMessageResult(context.Request.CreateResponse(HttpStatusCode.InternalServerError, "We apologize but an unexpected error occured. Please try again later."));
}
}
In WebApiConfig i added this line :
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
The the Application_AuthenticateRequest raise some errors but GlobalExceptionHandler is never reached.
Do you have any idea how can I solve this issue ?
Thanks in advance.
Application_AuthenticateRequest does not come in the Web API pipeline. So if an exception is thrown in this method those can be caught by the Web API exception handler, because the exception is thrown before the Web API pipeline is started.
There are two ways to do this:
Either change the authentication mechanism and use Web API Authentication(IAuthenticationFilter) instead of Application_AuthenticateRequest.
If this project has only Web API related controllers, not like MVC and all.
Or use Application_Error in the Global.asax.cs file to catch the exception thrown in Application_AuthenticateRequest
I maintain a web application that have a page with the JSF tag <f:event. I have rewrote a method in a service class for it to throw a business exception. However, when the business exception is thrown, it isn't caught in managed bean and the exception is showed on the page. Seems that my code try/catch doesn't work.
In XHTML:
<f:event listener="#{resourceBean.init(enrollment)}" type="preRenderView" />
Listener method in Managed Bean:
private boolean canCreateResource;
public void init(Enrollment enrollment) {
(...)
try {
canCreateResource = resourceService.canCreateResource(enrollment);
} catch (BusinessException e) {
canCreateResource = false;
}
}
Method in service class:
public boolean canCreateResource(Enrollment enrollment) {
if (...) {
if (mandateService.isCoordinator(user, course)) {
return true;
} else {
throw new BusinessException("Undefined business rule.");
}
}
return false;
}
From what I read on other sites, I suppose I have to implement some JSF's handler class. But which and how?
EDITED
OBS 1: The BusinessException class extends RuntimeException class.
OBS 2: The attribute canCreateResource was created to control the render of a button.
It's because you threw a RuntimeException from an EJB.
When such RuntimeException is not annotated with #ApplicationException, then the EJB container will wrap it in an javax.ejb.EJBException and rethrow it. This is done so because runtime exceptions are usually only used to indicate bugs in code logic, i.e. programmer's mistakes and not enduser's mistakes. You know, NullPointerException, IllegalArgumentException, IndexOutOfBoundsException, NumberFormatException and friends. This allows the EJB client to have one catch-all point for such runtime exceptions, like catch (EJBException e) { There's a bug in the service layer or in the way how we are using it! }
If you had tried catch (Exception e) and inspected the actual exception, then you'd have noticed that.
Fix your BusinessException class accordingly to add that annotation, it will then be recognized as a real application exception and not be wrapped in an EJBException:
#ApplicationException(rollback=true)
public class BusinessException extends RuntimeException {
// ...
}
Do note that in case you throw an non-RuntimeException, then you still need to keep the annotation on that, explicitly with rollback=true, because by default it wouldn't perform a rollback, on the contrary to a RuntimeException without the annotation.
#ApplicationException(rollback=true)
public class BusinessException extends Exception {
// ...
}
Summarized:
RuntimeException thrown from transactional EJB method will perform full rollback, but exception will be wrapped in EJBException.
RuntimeException with #ApplicationException from transactional EJB method will only perform full rollback when rollback=true is explicitly set.
Exception from transactional EJB method will not perform full rollback.
Exception with #ApplicationException from transactional EJB method will only perform full rollback when rollback=true is explicitly set.
Note that #ApplicationException is inherited over all subclasses of the custom exception, so you don't need to repeat it over all of them. Best would be to have it as an abstract class. See also the examples in the related question linked below.
See also:
Letting the presentation layer (JSF) handle business exceptions from service layer (EJB)
If isCoordinator method can eventually throw an exception you should add a try catch block inside canCreateResource method. You can throw your own exception or propagate the original one. In both cases you have to declare it in the method signature. If you throw BusinessException:
public void canCreateResource(Enrollment enrollment) throws BusinessException
Do not return any value. Or return a boolean value but do not throw any exception.
In the catch block inside the init method add the Facelet message exception:
...
} catch (BusinessException e) {
this.canCreateResource = false;
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), ""));
}
}
Also in your page you have to add <h:messages> tag.
In case you want to catch an exception that you did not create yourself (and you are not able to annotate with #ApplicationException), you can catch all exceptions and see if one of the causes is of the type you want to catch.
You can check the causes of the exception recursively:
public static <T extends Throwable> T getCauseOfType(final Throwable throwable,
final Class<T> type) {
if (throwable == null) {
return null;
}
return type.isInstance(throwable) ? (T) throwable : getCauseOfType(throwable.getCause(), type);
}
public static <T extends Throwable> boolean hasCauseOfType(final Throwable throwable,
final Class<T> type) {
return getCauseOfType(throwable, type) != null;
}
You can use this like:
try {
...
}
catch (Exception e) {
if (hasCauseOfType(e, SomeException.class)) {
// Special handling
}
else {
throw e;
}
}
The EJB method (using CMT) that updates an entity supplied :
#Override
#SuppressWarnings("unchecked")
public boolean update(Entity entity) throws OptimisticLockException {
// Code to merge the entity.
return true;
}
This will throw the javax.persistence.OptimisticLockException, if concurrent update is detected which is to be handled precisely by the caller (a managed bean).
public void onRowEdit(RowEditEvent event) {
try {
service.update((Entity) event.getObject())
} catch(OptimisticLockException e) {
// Add a user-friendly faces message.
}
}
But doing so makes an additional dependency from the javax.persistence API on the presentation layer compulsory which is a design smell leading to tight-coupling.
In which exception should it be wrapped so that the tight-coupling issue can be omitted in its entirely? Or is there a standard way to handle this exception which in turn does not cause any service layer dependencies to be enforced on the presentation layer?
By the way, I found it clumsy to catch this exception in the EJB (on the service layer itself) and then return a flag value to the client (JSF).
Create a custom service layer specific runtime exception which is annotated with #ApplicationException with rollback=true.
#ApplicationException(rollback=true)
public abstract class ServiceException extends RuntimeException {}
Create some concrete subclasses for general business exceptions, such as constraint violation, required entity, and of course optimistic lock.
public class DuplicateEntityException extends ServiceException {}
public class EntityNotFoundException extends ServiceException {}
public class EntityAlreadyModifiedException extends ServiceException {}
Some of them can be thrown directly.
public void register(User user) {
if (findByEmail(user.getEmail()) != null) {
throw new DuplicateEntityException();
}
// ...
}
public void addToOrder(OrderItem item, Long orderId) {
Order order = orderService.getById(orderId);
if (order == null) {
throw new EntityNotFoundException();
}
// ...
}
Some of them need a global interceptor.
#Interceptor
public class ExceptionInterceptor implements Serializable {
#AroundInvoke
public Object handle(InvocationContext context) throws Exception {
try {
return context.proceed();
}
catch (javax.persistence.EntityNotFoundException e) { // Can be thrown by Query#getSingleResult().
throw new EntityNotFoundException(e);
}
catch (OptimisticLockException e) {
throw new EntityAlreadyModifiedException(e);
}
}
}
Which is registered as default interceptor (on all EJBs) as below in ejb-jar.xml.
<interceptors>
<interceptor>
<interceptor-class>com.example.service.ExceptionInterceptor</interceptor-class>
</interceptor>
</interceptors>
<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>com.example.service.ExceptionInterceptor</interceptor-class>
</interceptor-binding>
</assembly-descriptor>
As a general hint, in JSF you can also have a global exception handler which just adds a faces message. When starting with this kickoff example, you could do something like this in YourExceptionHandler#handle() method:
if (exception instanceof EntityAlreadyModifiedException) { // Unwrap if necessary.
// Add FATAL faces message and return.
}
else {
// Continue as usual.
}
I developed a desktop application, it's almost done but still contains some bugs which I'm eliminating.
I use a general [try...catch] block wrapped around my application
[STAThread]
static void Main()
{
try
{
program = new Program();
// ...
}
catch (Exception x)
{
// ...
MessageBox.Show(
message,
Resources.MESSAGEBOX_ERROR_CRASH_Caption,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
my Program class constructor being:
public Program()
{
// [...]
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// [...]
frmLogon = new Logon();
Application.Run(frmLogon);
}
to ensure that any unhandled exception will bubble all the way up the stack and is at least responded to with some communicative message box.
It works fine when I run the application under Visual Studio (debug mode), but when I deployed it and installed on my PC, it doesn't - that's what I get when the bug (which I've already identified, by the way) causes it to read from a null array
Why? It baffles me really. Why was it "unhandled"? It was my understanding that try...catch should work regardless of whether it's release or debug mode, otherwise what would be the point.
This is kind of old, but if you still need a solution, you need to handle some events, enclosing the entire thing in a try catch won't work. Do something like this:
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
AppDomain.CurrentDomain.UnhandledException += ProcessAppException;
Application.ThreadException += ProcessThrException;
Application.Run(new MainForm());
}
private static void ProcessAppException(object sender, UnhandledExceptionEventArgs e)
{
XtraFunctions.LogException((Exception)e.ExceptionObject);
throw (Exception)e.ExceptionObject; //MessageBox in your case.
}
private static void ProcessThrException(object sender, ThreadExceptionEventArgs e)
{
XtraFunctions.LogException(e.Exception);
throw e.Exception; //MessageBox in your case.
}
When an exception isn't caught, it will go through one of those before displaying the exception dialog. So you have the option to override it and display a nice message of your choice.