WSDL & Exceptions

Is there any information online about how to process exceptions in WebServices on the client side? We have a number of controls bound to a WebService which can, of course, generate exceptions. But how does the client side find out details about them? I’m using HandleErrorHandler to set up an error handler for the data source, but the ErrorEvent object doesn’t give any information about exactly what went wrong.

You are provided the DSResponse, which contains the httpStatusCode, raw text of the response, etc.

But from where (if anywhere) can I get the exception itself?

I’m still having this problem. How can I get at the actual exception raised, and handle it the way I would normally handle an exception in java? All I can do at the moment is get at the status and the HttpResponseCode, which doesn’t really help me much - I flash up an error message but the exception is still getting raised and not handled by me.

You can’t use try/catch to catch an exception across a network boundary. Options are to use server-side exception handling or work with the HTTP response information in the DSResponse on the client side.

I understand that. However, it is standard WebService practice to raise exceptions - and as long as SmartGWT is using WebServices, it should be able to access the exceptions - shouldn’t it? If not, how can I actually get exception values passed back to the client side from the server side?

Here’s an excerpt from our WebService code:

    @WebMethod(operationName = "updateSystem")
    public void updateSystem(@WebParam(name = "system") SystemData system) throws WSFault
    {
        System newSystem = null;

        // Validate and transform input data
        try
        {
            if (system.getSystemId() > 0)
            {
                newSystem = new System();
                newSystem.setComments(system.getComments());
                newSystem.setSystemName(system.getSystemName());
                newSystem.setSystemId(system.getSystemId());
            }
            else
            {
                 throw new WSFault(ResponseStatus.STATUS_CODE_INVALID_INPUT,  
				                   ResponseStatus.STATUS_MESSAGE_INVALID_INPUT);
            }

        }
        catch (Exception e)
        {
            throw new WSFault(ResponseStatus.STATUS_CODE_SERVICE_ERROR, 
			                  ResponseStatus.STATUS_MESSAGE_SERVICE_ERROR, 
							  e);
        }

        try
        {
            Session session = null;
            SystemDAO theSystemDAO = null;

            session = HibernateUtil.getSessionFactory().getCurrentSession();
            theSystemDAO = new SystemDAO();
            theSystemDAO.setSession(session);

            // Begin transaction
            session.beginTransaction();

            // Update system
            theSystemDAO.makePersistent(newSystem);

            // All is well, end transaction
            session.getTransaction().commit();
        }
        catch (org.hibernate.ObjectNotFoundException onfe)
        {
            // This is expected result where no data found
            throw new WSFault(ResponseStatus.STATUS_CODE_NOT_FOUND, 
			                  ResponseStatus.STATUS_MESSAGE_NOT_FOUND);
        }
        catch (Exception e)
        {
            throw new WSFault(ResponseStatus.STATUS_CODE_MODEL_ERROR, 
			                  ResponseStatus.STATUS_MESSAGE_MODEL_ERROR, 
							  e);
        }
        return;
    }

And here’s an excerpt from our client-side code which invokes the WebService:

        buttonOK.addClickHandler(new ClickHandler()
        {
            public void onClick(ClickEvent event)
            {
                DSRequest dsr = new DSRequest();
                dsr.setWillHandleError(true);
                form.saveData(new DSCallback()
                {
                    public void execute(DSResponse response, Object rawData, DSRequest request)
                    {
                        if (response.getStatus() != DSResponse.STATUS_SUCCESS)
                        {
                            winSystem.destroy();
                            UIUtils.handleError("ToolBar.changeSystems",
                                                "An error occurred saving the input data.",
                                                response);
                        }
                        else
                        {
                            winSystem.destroy();
                            grid.invalidateCache();
                        }
                    }
                }, dsr);
            }
        });

All we can get from the client side is the DSResponse.getStatus() - which, if there is an error on the server side, is always -90. How can we send more meaningful data from the server side to the client side? Presumably the server side should populate the DSResponse object sent back - but how do we do that inside a WebService?

Something’s not getting through here - we said:

For some reason you are focusing on getStatus() - dsResponse has a number of APIs. You can get the raw text of the response, for one, as a means of communicating additional error information.

No, nothing’s “not getting through”. We have fully investigated the DSResponse, and it does not appear to hold the information we need.

For example, currently our WebService throws a particular exception (WSFault, as a WebService should), with certain application-specific parameters. But nothing to indicate any of these parameters is getting back to the client. Presumably it should be as part of the DSResponse object, but it’s not. We’ve investigated the result of DSResponse.getErrors(), and that’s empty. The result of DSResponse.getData() is one record, containing two attributes - ‘data’, which has the value of ‘Transport error - HTTP code: 500 for URL: /SystemWSService?WSDL’', and ‘status’ which has the value of -90. The DSResponse has no other useful attributes for us in this case. If I’m missing some API that will give us the information we need, please tell me what it is, as I can’t find it. There appears to be nothing else in the DSResponse for us to investigate.

So I’m guessing that perhaps we’re doing something wrong in the WebService - we’re not populating the DSResponse correctly. But I can find nothing online to indicate how a WebService does so.

If you are actually signaling 500 server error, this is generally taken to mean the server has failed to form a proper response at all, eg, it is not guaranteed to be in any format whatsoever (even, say, a stack trace). In this case, no attempt is made to parse the response into the DSResponse, however, the response is still available as the “rawData” parameter to any DSCallback.

If this is an ordinary, recoverable fault, you probably want to set up your service to return a well-formed XML response instead, and not signal an HTTP-level error (because the error is application level, not HTTP level).

As far as we can tell, we’re not ‘signalling’ any kind of error - the WebService is just throwing the standard exception. We assumed that SmartGWT is translating this into the 500 server error - our code doesn’t do it (as shown above).

Then, once again, how do you get the correct response from the rawData parameter?

Our WebService isn’t signalling an HTTP-level error. All it’s doing is throwing an exception, in the standard way that WebServices are supposed to do.

Is there no example of a WebService producing an exception and the client side interpreting the response anywhere online? We have been trying to work this out for weeks and the documentation is very poor in this area. At this stage we can’t even tell if our error/problem is in our WebService or on the client side. We are on the point of giving up using WebServices with our project because of the poor documentation on using them with SmartGWT.

You keep mentioning the idea of SmartGWT somehow receiving the exception, however, from what we understand, you have no SmartGWT code on the server that is involved in producing the HTTP response from your server. You may be using the HttpProxyServlet, but this is just a proxy, and is receiving an HTTP response from whatever system you are using to provide web services - it never receives an exception object directly.

Does this clear up the situation? There is no documentation on this because SmartGWT is not involved, at all. You have some third party web services stack producing an HTTP response from the exception you throw, and all SmartGWT ever receives is that HTTP response.

You can see what the HTTP response is by looking at the RPC tab in the Developer Console.

You can access it by casting “rawData” to String.

Casting ‘rawData’ to String was the first thing I tried - unfortunately doing so gives an exception:

Exception casting rawData to String ‘com.google.gwt.core.client.JavaScriptObject$ cannot be cast to java.lang.String’

Have you been able to look at the RPC tab in the Developer Console and see what your web service is returning as an HTTP response? Because with this talk about SmartGWT receiving exceptions, at this point we’re a little unclear on what your deployment model looks like. Do you have SmartGWT server code involved in processing (other than the HttpProxyServlet)?

Yes, the RPC tab in the Developer Console correctly shows the XML being returned in the Response section. I assumed this represents the data contained in the “rawData” object, but since we can’t get into the “rawData” object, I don’t know that it is. But it’s not in the DSResponse, so where else could it be?

As far as our deployment model goes, it’s pretty simple - we have a standard WebService (some code supplied above) and a standard SmartGWT implementation of binding a control (a ListGrid) to it. When the user takes a certain action, we pop up a dialog box allowing the user to change data in the row selected in the ListGrid (this just uses a DynamicForm bound to the same WebService and a save). That all works fine - unless we ‘manufacture’ an error (by trying to save invalid data). Then we get this problem with trying to get the error the WebService discovers back to the client side.

OK, good. What about the advice of not sending a 500 server error? This appears to be a normal web service fault, not something that should be reported as an HTTP-level error.

By reporting this as an HTTP level error, you are heading into an entirely different part of the processing chain intended for responses that are garbled - the response text is available by registering to hear about HTTP transport errors.

If you were instead returning any 200 error code, the response would go to the DataSource as usual, where you could turn it into validation errors (or whatever kind of error it is) in transformResponse(), as usual.

But we’re not reporting this as an HTTP level error. We’re not reporting it at all - the WebService simply throws an exception (as shown in the code supplied above). What I’m describing is what is happening at the client side in trying to interpret the response.

Once again, there’s no SmartGWT server code involved in the web service, right? SmartGWT does not receive the exception. Whatever web service software you’re using (Apache Axis or some other system) is what is receiving the exception and turning it into a 500 server error. SmartGWT is not involved until it receives the HTTP response.

So, options:

  1. get rid of the entire WSDL approach here. A far simpler, far more powerful approach would be to use either the built-in Hibernate connector or DMI. We’re not sure if you’ve been through the QuickStart Guide and looked at the advice and approaches there, but WSDL is recommended only if you have both pre-existing WSDL services and deep expertise with WSDL - the first two recommendations are using the SmartGWT Server Framework and using RestDataSource.

  2. implement your error handling via the handleTransportError system

  3. look into whether you can configure Axis (or whatever you’re using) to not send 500 server error for ordinary faults