Hi Craig,

You're welcome.

I looked into our generated code. I have a test for a database server. It uses 
our custom exception, but I think that just added an extra catch. If I set a 
breakpoint in my server implementation code, then look back down the stack 
frames, I find this:

  try {
    iface_->commitDatabaseChangesAndUnlock(result.success, args.commitStore, 
args.lockHandle); <-- My server function
    result.__isset.success = true;
  } catch (OurThriftExceptionWire &ex) {
    result.ex = ex;
    result.__isset.ex = true;
  } catch (const std::exception& e) {
    ::apache::thrift::TApplicationException x(e.what());
    oprot->writeMessageBegin("commitDatabaseChangesAndUnlock", 
::apache::thrift::protocol::T_EXCEPTION, seqid);
    x.write(oprot);
    oprot->writeMessageEnd();
    oprot->getTransport()->flush();
    oprot->getTransport()->writeEnd();
    return;
  }

so it looks like C++ server code does catch any std::exception and throw 
TApplicationException across the wire, with the what() from the original 
exception.

On the client side, I have something similar:
void 
MyClient::recv_commitDatabaseChangesAndKeepLock(std::vector<LsDoubleRelation> & 
_return)
{

  int32_t rseqid = 0;
  std::string fname;
  ::apache::thrift::protocol::TMessageType mtype;

  iprot_->readMessageBegin(fname, mtype, rseqid);
  if (mtype == ::apache::thrift::protocol::T_EXCEPTION) {
    ::apache::thrift::TApplicationException x;
    x.read(iprot_);
    iprot_->readMessageEnd();
    iprot_->getTransport()->readEnd();
    throw x;
  }
// The rest of the function is elided...

so on the client side it looks for a message type T_EXCEPTION. If it finds one, 
then it generates and throws a TApplicationException.

Now, what I don't quite get is that I thought I read some code earlier today 
that was detecting T_EXCEPTION, but was throwing TTransportException on the 
client side. But now I can't find that in our generated code.

- Rush

On May 17, 2012, at 5:21 PM, Craig Artley wrote:

> 
> Rush, thanks for the reply and the example code. I got that working with Java 
> server and clients without too much drama.
> 
> Still, when I read the THRIFT-378 
> (https://issues.apache.org/jira/browse/THRIFT-378) I get the feeling that it 
> should not be this tedious. Quoting from the original description:
> 
> ---
> One workaround might be to add an InternalError exception and declare
> every method to throw that, but this would require wrapping each method
> implementation on the server with a try/catch/throw block, which is 
> ugly and repetitive (especially if there are other try/catch blocks 
> nested) – exactly the sort of code that Thrift is good at automating 
> away.
> 
> 
> I think the right fix would be for the server to catch runtime 
> exceptions in the generated process methods and send back a 
> TApplicationException.  This is what some other servers (e.g., Erlang) 
> already do.---
> 
> So what we have done is essentially implement the "workaround": boilerplate 
> code in every method to explicitly catch internal problems and throw a custom 
> exception.
> 
> The original reporter really nailed in the last sentence - server should 
> automatically catch run time exceptions and throw TApplicationExceptions to 
> the client. The bug is marked as resolved for nearly 3 years, so I must be 
> completely misunderstanding the intent.  Isn't it supposed to automatically 
> toss back to the client a TApplicationException of type INTERNAL_ERROR? Do I 
> really have to do that by hand in every single method? Do people have to do 
> that with C++ servers too, or is this only a problem with Java servers?
> 
> It was supposedly fixed, but maybe I just don't understand the goal. When my 
> sever hits a run time exception, the client just gets a cryptic 
> TTransportException with no clue of what went wrong.
> 
> Regards,
>   -craig
> 
> ----------------------------------------
>> Subject: Re: Using TApplicationException - need example
>> From: [email protected]
>> Date: Thu, 17 May 2012 09:44:30 -0700
>> To: [email protected]
>> 
>> 
>> On May 16, 2012, at 10:07 PM, Steve Angelovich wrote:
>> 
>>> What is TApplicationException intended to be used for?
>>> 
>>> Is there a mechanism in thrift to propagate a runtime exception from the 
>>> server back to the client?
>>> 
>>> How do others deal with a case where the caller has made an RPC call with 
>>> an invalid argument. In Java I'd normally throw a IllegalArgumentException, 
>>> is something like this possible in thrift or do I have to add another user 
>>> defined exception type which will result in the caller having to deal with 
>>> another checked exception.
>>> 
>>> 
>>> Thanks for any help
>>> Steve
>>> 
>>> 
>>> On 05/10/2012 05:06 PM, Craig Artley wrote:
>>> 
>>> 
>>> Hello, I want to take exceptions on the server and return them back to the 
>>> client. For example, say the client's request leads to a run time exception 
>>> like IllegalArgumentException. I think that TApplicationException with 
>>> INTERNAL_ERROR is the way to do this, but I couldn't get it to work and I 
>>> can't find any examples.
>>> 
>>> In my service handler, I check the args. If they are invalid, I threw a 
>>> TApplicationException like this
>>> throw new 
>>> TApplicationException(TApplicationException.INTERNAL_ERROR,"Illegal 
>>> argument blah blah");
>>> 
>>> It still showed up in my client as a TTransportException:
>>> org.apache.thrift.transport.TTransportException
>>> at 
>>> org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:132)
>>> 
>>> I'm sure I am just misunderstanding something. What am I missing? Can 
>>> anyone provide an example of robust exception handling in the server to the 
>>> client?
>>> 
>>> I read about it in THRIFT-378
>>> https://issues.apache.org/jira/browse/THRIFT-378
>>> 
>>> Regards,
>>> -craig
>> 
>> You can define your own exception types. Here's a real world example:
>> 
>> -----------------------------------------------------
>> File OurThriftException.thrift:
>> struct ExceptionInfoAll
>> {
>> 1: string name, /** The class name of the exception */
>> 2: string message, /** If not a OurCustomException, this is the result of 
>> what().
>> If OurCustomException, this is the result of ex.toString(). */
>> }
>> 
>> struct ExceptionInfoOurCustomException
>> {
>> 1: string errorCodeName, /** The name of the error, such as DeviceNotFound
>> This is specific to the exception class */
>> 2: i32 errorCodeValue, /** The numeric value of the error code */
>> 3: string description, /** The description string defined in the exception's 
>> EXCEPTION_TABLE */
>> 4: string debugMsg, /** The debug message attached to the exception at the 
>> throw point */
>> 5: string location, /** The location information that may have been attached 
>> at the throw point. */
>> }
>> 
>> struct ExceptionInfo
>> {
>> 1: ExceptionInfoAll defaultInfo, /** Available for any exception */
>> 2: optional ExceptionInfoOurCustomException ourInfo, /** Available only for 
>> OurCustomException */
>> }
>> 
>> exception OurThriftExceptionWire
>> {
>> 1: ExceptionInfo excInfo, /** The information extracted from the exception 
>> that was caught by the server. */
>> }
>> -----------------------------------------------------
>> 
>> 
>> Once you do this, you can define a service like this:
>> 
>> service MyService
>> {
>> returnType myCall ( args here) throws (1: OurThriftException. 
>> OurThriftExceptionWire ex),
>> }
>> 
>> On your server side, you do this (I have neglected to set the __isset values 
>> to make the code clearer.):
>> 
>> returnType myCall_handler (args)
>> {
>> 
>> try
>> {
>> do something
>> }
>> catch (OurCustomException &ex)
>> {
>> OurThriftExceptionWire wireEx;
>> 
>> wireEx.excInfo.defaultInfo = "OurCustomException";
>> wireEx.excInfo.defaultInfo.message = ex.what();
>> 
>> // Extract info from OurCustomException here and pack into 
>> wireEx.excInfo.ourInfo
>> 
>> throw wireEx;
>> }
>> catch (std::exception &ex)
>> {
>> OurThriftExceptionWire wireEx;
>> 
>> wireEx.excInfo.defaultInfo = "OurCustomException";
>> wireEx.excInfo.defaultInfo.message = ex.what();
>> 
>> throw wireEx;
>> 
>> }
>> }
>> 
>> And on your client side you do this:
>> 
>> try
>> {
>> myCall (args)
>> }
>> catch (OurThriftExceptionWire &ex)
>> {
>> // Now you have exception details from the server side. We would throw an 
>> exception type
>> // specific to the library where this code is, but it would contain all the 
>> info from
>> // ex.excInfo.
>> }
>> 
>> I hope that helps.
>> 
>> Best regards,
>> Rush
>                                         

Reply via email to