Catching Message Handling Exceptions with the #Exceptionhandler - axon

I have two application e.g) A, B
A has a Saga
B is just web application
A sent Command messages to B and
B sent exception for that Command to A's Saga and A's Saga received well
and B have a #ExceptionHandler which I hope to be invoked but it's not working
How can I make them be invoked?
EDIT
this is A application's Saga that sends command messages to B application
and deals with exception which B sent
#Saga
public class OrderSaga {
#Autowired
private transient CommandGateway commandGateway;
#StartSaga
#SagaEventHandler(associationProperty = "orderId")
public void handle(CreateOrderEvent evt) {
String paymentId = UUID.randomUUID().toString();
SagaLifecycle.associateWith("paymentId", paymentId);
commandGateway.send(new CreatedPaymentCommand(paymentId, evt.getUserId(),evt.getFoodPrice())).exceptionally(exp -> {
System.out.println("got it");
System.out.println(exp.getMessage());
return null;
});
}
}
this is B application that throws exception for test
#Aggregate
#NoArgsConstructor
public class PaymentAggregate {
#AggregateIdentifier
private String paymentId;
private String userId;
private PaymentStatus status;
#CommandHandler
public PaymentAggregate(CreatedPaymentCommand cmd) {
throw new IllegalStateException("this exception was came from payment aggregates");
// AggregateLifecycle.apply(new CreatedPaymentEvent(cmd.getPaymentId(),
// cmd.getUserId(),cmd.getMoney()));
}
#ExceptionHandler(resultType = IllegalStateException.class)
public void error(IllegalStateException exp) {
System.out.println(exp.getMessage());
}
// I want this #ExceptionHandler to be invoked
#EventSourcingHandler
public void on(CreatedPaymentEvent evt) {
this.paymentId = evt.getPaymentId();
this.userId = evt.getUserId();
}
}
A application catch exception well like below
2021-08-24 11:46:43.534 WARN 14244 --- [ault-executor-2] o.a.c.gateway.DefaultCommandGateway : Command 'com.common.cmd.CreatedPaymentCommand' resulted in org.axonframework.commandhandling.CommandExecutionException(this exception was came from payment aggregates)
got it
this exception was came from payment aggregates
but B is not I thought that B's #ExceptionHandler will catch that exception
in short, How can I make B's #ExceptionHandler to be invoked

It doesn't work right now because the exception is thrown from the constructor of your aggregate.
As you are using a constructor command handler, there is no instance present yet.
And without an instance, Axon Framework cannot spot the #ExceptionHandler annotated method you've set up.
This is the only missing point for the exception handlers at this stage. Honestly, the reference guide should be a bit more specific about this. I am sure this will change in the future, though.
There's a different approach for having a command handler that constructs the aggregate and that can use the #ExceptionHandler: with the #CreationPolicy annotation. The reference guide has this to say about it, by the way.
Thus, instead of having a constructor command handler, you would set up a regular command handler using the AggregateCreationPolicy.ALWAYS.
That would adjust your sample like so:
#Aggregate
#NoArgsConstructor
public class PaymentAggregate {
#AggregateIdentifier
private String paymentId;
private String userId;
private PaymentStatus status;
#CommandHandler
#CreationPolicy(AggregateCreationPolicy.ALWAYS)
public void handle(CreatedPaymentCommand cmd) {
throw new IllegalStateException("this exception was came from payment aggregates");
// AggregateLifecycle.apply(new CreatedPaymentEvent(cmd.getPaymentId(),
// cmd.getUserId(),cmd.getMoney()));
}
#ExceptionHandler(resultType = IllegalStateException.class)
public void error(IllegalStateException exp) {
System.out.println(exp.getMessage());
}
// I want this #ExceptionHandler to be invoked
#EventSourcingHandler
public void on(CreatedPaymentEvent evt) {
this.paymentId = evt.getPaymentId();
this.userId = evt.getUserId();
}
}
Please give this a try in your application, #YongD.

Related

Aggregate identifier must be non-null after applying an event. Make sure the aggregate identifier is initialized at the latest

I am getting the below error. Axon version 3.3.3
org.axonframework.eventsourcing.IncompatibleAggregateException:
Aggregate identifier must be non-null after applying an event. Make
sure the aggregate identifier is initialized at the latest when
handling the creation event.
I have created a UserAggregate. It contains 2 events:
UserCreated
UpdateUserEvent
I am able to generate the first (UserCreated) event and it was saved in the event store with sequence 0, But while generating the second event I got the above-mentioned error.
Any suggestions on this?
UserAggregate.java
#Aggregate
public class UserAggregate {
#AggregateIdentifier
private String id;
private String email;
private String password;
public UserAggregate(String id, String email, String password) {
super();
this.id = id;
this.email = email;
this.password = password;
}
#CommandHandler
public UserAggregate(CreateUser cmd) {
AggregateLifecycle.apply(new UserCreated(cmd.getId(), cmd.getEmail(), cmd.getPassword()));
}
#CommandHandler
public void handle(UpdateUserCmd cmd) {
AggregateLifecycle.apply(new UpdateUserEvent(cmd.getId(), cmd.getEmail(),""));
}
#EventSourcingHandler
public void userCreated(UserCreated event) {
System.out.println("new User: email " + event.getEmail() +" Password: "+ event.getPassword());
setId(event.getId());
}
#EventSourcingHandler
public void updateUserEvent(UpdateUserEvent event) {
System.out.println("new User: email " + event.getEmail());
setId(event.getId());
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public UserAggregate() {
}
}
I am still getting to know Axon but here's how I managed to resolve the issue. Basically what the error is saying is that, when the UserAggregate is being instantiated the aggregate identifier (aka Primary Key) must not be null and must have a value.
There sequence of the life-cycle is that
It calls a no args constructor
It calls the constructor with your initial command, in your case. At this point, your aggregate identifier is still null and we will assign a value in the next step
It then calls a EventSourcingHandler that handles the event your applied from the previous step
Based on the steps above here's what you need to do:
Create a no args constructor
protected UserAggregate() {
}
Create a constructor which handles your first command:
#CommandHandler
public UserAggregate(CreateUser cmd) {
AggregateLifecycle.apply(
new UserCreated(cmd.getId(),cmd.getEmail(),cmd.getPassword()));
}
Finally add an event sourcing handler to handle the UserCreated event
#EventSourcingHandler
public void on(UserCreated userCreated) {
// this is where we instantiate the aggregate identifier
this.id = userCreated.getId();
//assign values to other fields
this.email = userCreated.getEmail();
this.password = userCreated.getPassword();
}
And here's the complete example:
#Aggregate
public class UserAggregate {
#AggregateIdentifier
private String id;
private String password;
private String email;
protected UserAggregate() {
}
#CommandHandler
public UserAggregate(CreateUser cmd) {
AggregateLifecycle.apply(
new UserCreated(cmd.getId(),cmd.getEmail(),cmd.getPassword()));
}
#EventSourcingHandler
public void on(UserCreated userCreated) {
// this is where we instantiate the aggregate identifier
this.id = userCreated.getId();
//assign values to other fields
this.email = userCreated.getEmail();
this.password = userCreated.getPassword();
}
}
When you are following the Event Sourcing paradigm for your Aggregates, I'd typically suggest two types of constructors to be present in the code:
A default no-arg constructor with no settings in it.
One (or more) constructor(s) which handles the 'Aggregate creation command'
In your example I see a third constructor to set id, email and password.
My guess is that this constructor might currently obstruct the EventSourcedAggregate implementation for correct validation.
The exception you are receiving can only occur if the #AggregateIdentifier annotated field is not set after the constructor command handler (in your case UserAggregate(CreateUser) has ended it's Unit of Work.
Thus, seeing your code, my only hunch is this "wild, unused" constructor which might obstruct things.
Lastly, I need to recommend you to use a more recent version of Axon.
3.3.3 is already quite far away from the current release, being 4.2.
Additionally, no active development is taking place on Axon 3.x versions.
It is thus wise to upgrade the version, which I assume shouldn't be a big deal as you are still defining your Command Model.
Update
I've just closed the Framework issue you've opened up. Axon provides entirely different means to tie in to the Message dispatching and handling, giving you cleaner intercept points than (Spring) AOP.
If you following the suggested guidelines to use a MessageDispatchInterceptor/MessageHandlerInterceptor or the more fine grained option with HandlerEnhancer, you can achieve these cross-cutting concerns you are looking for.
As far as logging goes, the framework even provides a LoggingInterceptor to do exactly what you need. No AOP needed.
Hope this helps you out Narasimha.
Thank you #Steven for the response.
I am able to reproduce this issue with Axon 4.2(latest) version also.
After removing the below AOP code in my project, The issue solved automatically.
Looks like Axon is missing compatible with the AOP feature.
AOP Code:
#Around("execution(* com.ms.axonspringboot..*(..))")
public Object methodInvoke(ProceedingJoinPoint jointPoint) throws Throwable {
LOGGER.debug(jointPoint.getSignature() + "::: Enters");
Object obj = jointPoint.proceed();
LOGGER.debug(jointPoint.getSignature() + "::: Exits");
return obj;
}
Axon 4.2 version error logs
2019-10-07 12:52:41.689 WARN 31736 --- [ault-executor-0] o.a.c.gateway.DefaultCommandGateway : Command 'com.ms.axonspringboot.commands.UpdateUserCmd' resulted in org.axonframework.commandhandling.CommandExecutionException(Aggregate identifier must be non-null after applying an event. Make sure the aggregate identifier is initialized at the latest when handling the creation event.)
2019-10-07 12:52:41.710 ERROR 31736 --- [nio-7070-exec-3] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exception
org.axonframework.axonserver.connector.command.AxonServerRemoteCommandHandlingException: An exception was thrown by the remote message handling component: Aggregate identifier must be non-null after applying an event. Make sure the aggregate identifier is initialized at the latest when handling the creation event.
at org.axonframework.axonserver.connector.ErrorCode.lambda$static$8(ErrorCode.java:84) ~[axon-server-connector-4.2.jar:4.2]
at org.axonframework.axonserver.connector.ErrorCode.convert(ErrorCode.java:180) ~[axon-server-connector-4.2.jar:4.2]

SoapFault handling with Spring WS client - WebServiceGatewaySupport and WebServiceTemplate

I am trying to write a Spring WS client using WebServiceGatewaySupport. I managed to test the client for a successful request and response. Now I wanted to write test cases for soap faults.
public class MyClient extends WebServiceGatewaySupport {
public ServiceResponse method(ServiceRequest serviceRequest) {
return (ServiceResponse) getWebServiceTemplate().marshalSendAndReceive(serviceRequest);
}
#ActiveProfiles("test")
#RunWith(SpringRunner.class)
#SpringBootTest(classes = SpringTestConfig.class)
#DirtiesContext
public class MyClientTest {
#Autowired
private MyClient myClient;
private MockWebServiceServer mockServer;
#Before
public void createServer() throws Exception {
mockServer = MockWebServiceServer.createServer(myClient);
}
}
My question is how do i stub the soap fault response in the mock server, so that my custom FaultMessageResolver will be able to unmarshall soap fault?
I tried couple of things below, but nothing worked.
// responsePayload being SoapFault wrapped in SoapEnvelope
mockServer.expect(payload(requestPayload))
.andRespond(withSoapEnvelope(responsePayload));
// tried to build error message
mockServer.expect(payload(requestPayload))
.andRespond(withError("soap fault string"));
// tried with Exception
mockServer.expect(payload(requestPayload))
.andRespond(withException(new RuntimeException));
Any help is appreciated. Thanks!
Follow Up:
Ok so, withSoapEnvelope(payload) I managed to get the controller to go to my custom MySoapFaultMessageResolver.
public class MyCustomSoapFaultMessageResolver implements FaultMessageResolver {
private Jaxb2Marshaller jaxb2Marshaller;
#Override
public void resolveFault(WebServiceMessage message) throws IOException {
if (message instanceof SoapMessage) {
SoapMessage soapMessage = (SoapMessage) message;
SoapFaultDetailElement soapFaultDetailElement = (SoapFaultDetailElement) soapMessage.getSoapBody()
.getFault()
.getFaultDetail()
.getDetailEntries()
.next();
Source source = soapFaultDetailElement.getSource();
jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setContextPath("com.company.project.schema");
Object object = jaxb2Marshaller.unmarshal(source);
if (object instanceof CustomerAlreadyExistsFault) {
throw new CustomerAlreadyExistsException(soapMessage);
}
}
}
}
But seriously!!! I had to unmarshall every message and check the instance of it. Being a client I should be thorough with all possible exceptions of the service here, and create custom runtime exceptions and throw it from the resolver. Still at the end, its been caught in WebServiceTemplate and re thrown as just a runtime exception.
You could try with something like this:
#Test
public void yourTestMethod() // with no throw here
{
Source requestPayload = new StringSource("<your request>");
String errorMessage = "Your error message from WS";
mockWebServiceServer
.expect(payload(requestPayload))
.andRespond(withError(errorMessage));
YourRequestClass request = new YourRequestClass();
// TODO: set request properties...
try {
yourClient.callMethod(request);
}
catch (Exception e) {
assertThat(e.getMessage()).isEqualTo(errorMessage);
}
mockWebServiceServer.verify();
}
In this part of code mockWebServiceServer represents the instance of MockWebServiceServer class.

WF 4 OnUnhandledException not hit

I've created a custom activity which contains as a Body another Activity.
[Browsable(false)]
public Activity Body { get; set; }
protected override void Execute(NativeActivityContext context)
{
ActivityInstance res = context.ScheduleActivity(Body, new CompletionCallback(OnExecuteComplete), OnFaulted);
}
private void OnFaulted(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom)
{
throw new Exception(propagatedException.Message);
}
When an exception is thrown during the execution of the Body, ma handler for the OnFaulted is hit.
My execution starts with a call to static method Run of the WorkflowApplication class. My WorkflowApplication instance has a handler associated for the OnUnhandledException event.
instance.OnUnhandledException +=
delegate(WorkflowApplicationUnhandledExceptionEventArgs args)
{
Console.WriteLine(args.ExceptionSource);
waitEvent.Set();
return UnhandledExceptionAction.Cancel;
};
But regardless of what happens when the Activity hosted in the Body is executed, i never reach the handler defined above. I thought that if i throw an exception from the OnFaulted, i will be able to redirect the flow to the OnUnhandledException but i was wrong. Any ideas ?
I need this in order to centralize my errors, check them and display messages accordingly. Also i need a way to stop the execution and so on and i don't want to define handlers all over the application. Is there any way to accomplish this ?
As Will suggested, i will post what i did to handle my scenario.
Basically, in my custom activity i have hosted an Assign :
[Browsable(false)]
public Activity Body { get; set; }
Activity System.Activities.Presentation.IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
{
return new Assignment()
{
Body = new Assign() { DisplayName = "" }
};
}
I've added this code to my Execute method :
ActivityInstance res = context.ScheduleActivity(Body, new CompletionCallback(OnExecuteComplete), OnFaulted);
I was trying to run this Assignment by giving an array a negative value as index and and an exception was thrown. This, somehow ended my execution but no handler for the events of my WorkflowApplication instance were hit.
Here is the method given as a callback when executing the body ( in our case the Assign activity ) :
private void OnFaulted(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom)
{
faultContext.HandleFault();
CommunicationExtension ce = faultContext.GetExtension<CommunicationExtension>();
ITextExpression toTextExpression = (propagatedFrom.Activity as Assign).To.Expression as ITextExpression;
string valueTextExpression = string.Empty;
if ((propagatedFrom.Activity as Assign).Value != null)
{
if ((propagatedFrom.Activity as Assign).Value.Expression != null)
valueTextExpression = (propagatedFrom.Activity as Assign).Value.Expression.ToString();
}
if (ce != null)
{
ce.AddData(string.Format("{0} found on Assignment definition [{1} = {2}]", propagatedException.Message, toTextExpression.ExpressionText, valueTextExpression));
}
}
The trick was to call :
faultContext.HandleFault();
and use CommunicationExtension to allow me to to display the erros in the GUI.
The code for this class is trivial :
public class CommunicationExtension
{
public List<string> Messages { get; set; }
public CommunicationExtension()
{
Messages = new List<string>();
}
public void AddData(string message)
{
if (string.IsNullOrEmpty(message))
return;
Messages.Add(message);
}
}
Use this to add the extension:
CommunicationExtension ce = new CommunicationExtension();
instance.Extensions.Add(ce);
where instance is my WorkflowApplication instance.
I understood that for each instance of the workflow application we have one instance of its extension class. So i can send messages like this from all my custom activities in order to display their status.
I hope this scenario can help other people too.

JBoss 7: No EJB receiver available

I'm pretty newbie with JBoss 7. I'm facing strange behaviour. Sometimes, when I try to invoke a session bean, I run into the following exception:
com.google.gwt.user.server.rpc.UnexpectedException: Service method 'public abstract java.util.List myServlet.getData() throws myException' threw an unexpected exception: java.lang.IllegalStateException: No EJB receiver available for handling [appName:myAppNameEE,modulename:myModuleEJB,distinctname:] combination for invocation context org.jboss.ejb.client.EJBClientInvocationContext#3e23bd28
It usually happens when run my GWT application from Eclipse. The exception does not occur always. Sometimes it occurs fewer than others. Sometimes it occurs pretty every time I call a session bean and it's a pain. I read tutorial (https://docs.jboss.org/author/display/AS71/EJB+invocations+from+a+remote+client+using+JNDI?_sscc=t) and I am pretty sure to have the jboss-ejb-client.properties in the right place.
my jboss-ejb-client looks like:
endpoint.name=myAppEE/myAppEJB
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=localhost
remote.connection.default.port = 4447
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
it is located in:
myAppEJB\ejbModule\com\myApp\ejb\conf
The businness delegate:
public class myAppServerDelegate extends ServerDelegate{
private Logger logger = Logger.getLogger(myAppServerDelegate.class.getName());
private myAppRemote theSession = null;
public myAppServerDelegate() throws Exception {
try {
theSession = (myAppRemote) getJndiContext().lookup(getJindiLookupName(myAppServerDelegate.class, myAppRemote.class));
} catch (NamingException e) {
throw (e);
}
}
public List<myDataDTO> getAllmyDataBy(String a, String b,
String c, String d,Integer e,
Integer f) throws ServerDelegateException {
return theSession.getAllmyDataBy(a, b, c, d,e,f);
}
public Integer getCountmyDataBy(String a, String b, String c, String d) throws ServerDelegateException {
return theSession.getCountmyDataBy(a, b, c, d);
}
...
public String getServiceMessage() {
return theSession.getServiceMessage();
}
...
}
The session bean:
#Stateless
public class myAppSession implements myAppRemote {
private Logger logger = Logger.getLogger(myAppSession.class.getName());
#PersistenceContext
protected EntityManager entityManager;
#EJB
private myAppHomeLocal beanmyApp;
...
public String getServiceMessage() {
return "MESSAGGIODISERVIZIO";
}
public List<myDataDTO> getAllmyDataBy(String a,String b,
String c, String d,Integer e,
Integer f) throws ServerDelegateException {
logger.info("myAppSession.getAllmyDataBy.");
List<myData> entityList = findByParms(a, b, c, d,e,f);
return myDataAssemblyDTO.getmyDataDTOList(entityList);
}
public Integer getCountmyDataBy(String a,String b, String c, String d) throws ServerDelegateException {
return findByParmsCount(a, b, c, d);
}
...
}
The servlet:
...
#SuppressWarnings("serial")
public class MyGenericServiceImpl extends RemoteServiceServlet implements MyGenericService {
private MyAppServerDelegate myAppServerDelegate = null;
public MyGenericServiceImpl() throws Exception{
super();
myAppServerDelegate = new MyAppServerDelegate();
}
private MyAppServerDelegate getDelegate() {
return myAppServerDelegate;
}
private myGWTException buildLocalExceptionFromServerException(ServerDelegateException sde) {
myGWTException x = new myGWTException();
x.setParms(sde.guiMessage,sde.timestamp,sde.tipoEvento);
return x;
}
#Override
public PagingLoadResult<myDataBean> getAllmyDataBy(String a, String b, String c, PagingLoadConfig plc) throws MyGWTException {
try {
String cs = ((UserSessionBean)this.getThreadLocalRequest().getSession().getAttribute("user")).getCodiceStudio();
List<myDataBean> tsb = MyDataClientAssembly.getMyDataBeanList(myAppServerDelegate.getAllmyDataBy(cs, a, b, c, plc.getOffset(), plc.getLimit()));
return new BasePagingLoadResult<MyDataBean>(tsb, plc.getOffset(), myDataServerDelegate.getCountmyDataBy(cs, a, b, c));
} catch (ServerDelegateException sde) {
throw buildLocalExceptionFromServerException(sde);
}
}
#Override
public String getServiceMessage() {
return getDelegate().getServiceMessage();
}
#Override
public Integer getCountmyDataBy(String a, String b, String c) throws AmbrogioGWTException {
try {
String cs = ((UserSessionBean)this.getThreadLocalRequest().getSession().getAttribute("user")).getCs();
return myAppServerDelegate.getCountmtDataBy(cs, a, b, c);
} catch (ServerDelegateException sde) {
throw buildLocalExceptionFromServerException(sde);
}
}
}
The serverdelegate:
public class ServerDelegate {
static public String getJindiLookupName( Class<?> theBeanClass, Class<?> theSessionClass) throws NamingException {
String jbossServerName = System.getProperty("jboss.server.name");
if (jbossServerName== null || "".equals(jbossServerName)){
return "myAppEE/myAppEJB/"+ theBeanClass.getSimpleName() + "!" + theSessionClass.getName();
}else{
return "java:global/myAppEE/myAppEJB/" + theBeanClass.getSimpleName() + "!" + theSessionClass.getName();
}
}
static public Context getJndiContext() throws NamingException {
System.out.println("ServerDelegate.getJndiContext");
final Properties jndiProperties = new Properties();
String jbossServerName = System.getProperty("jboss.server.name");
if (jbossServerName== null || "".equals(jbossServerName)){
jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, org.jboss.naming.remote.client.InitialContextFactory.class.getName());
jndiProperties.put(Context.PROVIDER_URL, "remote://localhost:4447");
jndiProperties.put("jboss.naming.client.ejb.context", true);
jndiProperties.put("jboss.naming.client.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT", "false");
}
return new InitialContext(jndiProperties);
}
}
I can't figure out what's going on. TIA.
Francesco
It could be an issue of using the Local vs Remote interface or a connection issue.
Try enabling the TRACE log level on the org.jboss.ejb.client package. If you already have a log4j.properties in your classpath, include this line:
log4j.logger.org.jboss.ejb.client=TRACE
This post includes other clues for debugging the JBoss EJB client.
There is a list of dependencies I had to include in my mavens pom.xml file. These include
jboss-ejb-client
xnio-nio
jboss-remote-naming
jboss-sasl
jboss-transaction-ap
jboss-marshalling
jboss-marshalling-river
I was able to successfully make my remote request from my standalone java client however I do see the following issues
Jboss 7.1:
13:43:25,028 INFO [stdout] (EJB default - 6) Hello world
13:43:25,825 ERROR [org.jboss.remoting.remote.connection] (Remoting "mycomputername" read-1) JBREM000200: Remote connection failed: java.io.IOException: An existing connection was forcibly closed by the remote host
My java ejb client:
WARN [Remoting "client-endpoint" task-2] (ChannelAssociation.java:320) - Unsupported message received with header 0xffffffff
Reading online people seem to have solved this by updating the version of certain jars. However, I have yet to find a successful solution.
Update: I included the jboss-client.jar in my classpath when deploying a standalone ejb client. Works great.
java -classpath "$JBOSS_HOME/bin/client/jboss-client.jar;./my-ejb-client.jar" com.test.Myclient
In the class MyAppServerDelegate, you will have to instantiate the attribute "theSession" in the following methods rather than in the constructor:
getAllmyDataBy();
getCountmyDataBy();
getServiceMessage();
The result of theSession method invocation should be stored in a temporary variable, and afterwards call getJndiContext().close(), and then return the temporary value.
Every connection has to be explicitly closed. Indeed, the exception occurs when the maximum number of allowed connections has been reached.

Globally log exceptions from ASP.NET [ScriptService] services

I'm using the [System.Web.Script.Services.ScriptService] tag to use web services callable from client side javascript. What I need is a way of globally logging any unhandled exceptions in those methods. On the client side, I get the error callback and can proceed from there, but I need a server-side catch to log the exception.
The guy at this url:
http://ayende.com/Blog/archive/2008/01/06/ASP.Net-Ajax-Error-Handling-and-WTF.aspx
suggests that this can't be done.
Is that accurate? Do I seriously have to go to every single webmethod in the entire system and try/catch the method as a whole.
You can use an HTTP module to capture the exception message, stack trace and exception type that is thrown by the web service method.
First some background...
If a web service method throws an exception the HTTP response has a status code of 500.
If custom errors are off then the web
service will return the exception
message and stack trace to the client
as JSON. For example:{"Message":"Exception
message","StackTrace":" at
WebApplication.HelloService.HelloWorld()
in C:\Projects\Stackoverflow
Examples\WebApplication\WebApplication\HelloService.asmx.cs:line
22","ExceptionType":"System.ApplicationException"}
When custom errors are on then the
web service returns a default message
to the client and removes the stack
trace and exception type:{"Message":"There was an error processing the request.","StackTrace":"","ExceptionType":""}
So what we need to do is set custom errors off for the web service and plug in an HTTP module that:
Checks if the request is for a web service method
Checks if an exception was thrown - that is, a status code of 500 is being returned
If 1) and 2) are true then get the original JSON that would be sent to the client and replace it with the default JSON
The code below is an example of an HTTP module that does this:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
public class ErrorHandlerModule : IHttpModule {
public void Init(HttpApplication context) {
context.PostRequestHandlerExecute += OnPostRequestHandlerExecute;
context.EndRequest += OnEndRequest;
}
static void OnPostRequestHandlerExecute(object sender, EventArgs e) {
HttpApplication context = (HttpApplication) sender;
// TODO: Update with the correct check for your application
if (context.Request.Path.StartsWith("/HelloService.asmx")
&& context.Response.StatusCode == 500) {
context.Response.Filter =
new ErrorHandlerFilter(context.Response.Filter);
context.EndRequest += OnEndRequest;
}
}
static void OnEndRequest(object sender, EventArgs e) {
HttpApplication context = (HttpApplication) sender;
ErrorHandlerFilter errorHandlerFilter =
context.Response.Filter as ErrorHandlerFilter;
if (errorHandlerFilter == null) {
return;
}
string originalContent =
Encoding.UTF8.GetString(
errorHandlerFilter.OriginalBytesWritten.ToArray());
// If customErrors are Off then originalContent will contain JSON with
// the original exception message, stack trace and exception type.
// TODO: log the exception
}
public void Dispose() { }
}
This module uses the following filter to override the content sent to the client and to store the original bytes (which contain the exception message, stack trace and exception type):
public class ErrorHandlerFilter : Stream {
private readonly Stream _responseFilter;
public List OriginalBytesWritten { get; private set; }
private const string Content =
"{\"Message\":\"There was an error processing the request.\"" +
",\"StackTrace\":\"\",\"ExceptionType\":\"\"}";
public ErrorHandlerFilter(Stream responseFilter) {
_responseFilter = responseFilter;
OriginalBytesWritten = new List();
}
public override void Flush() {
byte[] bytes = Encoding.UTF8.GetBytes(Content);
_responseFilter.Write(bytes, 0, bytes.Length);
_responseFilter.Flush();
}
public override long Seek(long offset, SeekOrigin origin) {
return _responseFilter.Seek(offset, origin);
}
public override void SetLength(long value) {
_responseFilter.SetLength(value);
}
public override int Read(byte[] buffer, int offset, int count) {
return _responseFilter.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count) {
for (int i = offset; i < offset + count; i++) {
OriginalBytesWritten.Add(buffer[i]);
}
}
public override bool CanRead {
get { return _responseFilter.CanRead; }
}
public override bool CanSeek {
get { return _responseFilter.CanSeek; }
}
public override bool CanWrite {
get { return _responseFilter.CanWrite; }
}
public override long Length {
get { return _responseFilter.Length; }
}
public override long Position {
get { return _responseFilter.Position; }
set { _responseFilter.Position = value; }
}
}
This method requires custom errors to be switched off for the web services. You would probably want to keep custom errors on for the rest of the application so the web services should be placed in a sub directory. Custom errors can be switched off in that directory only using a web.config that overrides the parent setting.
You run the Stored Procedure in the backend. Then, for a single variable, it returns more than 1 value. Because of that, a conflicts occurs, and, this error is thrown.
I know this doesn't answer the question per-say, but I went on my own quest a while back to find this out and would up empty handed. Ended up wrapping each web service call in a try/catch, and the catch calls our error logger. Sucks, but it works.
In ASP.Net it is possible to catch all run handled exceptions using a global error handler although the blog post suggest this would not work but you could experiment with this approach trying to rethrow the error in some way?
Another idea would be to look at the open source elmah (Error Logging Modules and Handlers) for ASP.Net that might help or someone in that community may have an idea.

Resources