incubator-kato-spec mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Stuart Monteith <stuk...@stoo.me.uk>
Subject Re: Kato API javadoc - error handling
Date Wed, 15 Apr 2009 17:55:21 GMT
That addresses the issue.
The last time I has to call an API with the visitor API I ended up using 
an unchecked exception to abort it if the visit wasn't going to be 
interesting. I did feel ashamed about doing that though :-)

Nicholas Sterling wrote:
> How about having visitObject return a boolean saying whether traversal 
> should continue?
>
> Nicholas
>
>
> Stuart Monteith wrote:
>> I think with the visitor pattern we'd have to weigh up the typical 
>> usage patterns against the loss of control it implies.
>> Certainly for the heap, you might typically do a linear scan as the 
>> locality of objects implies nothing about their relationship to one 
>> another - using a bidirectional cursor is pointless.. The loss of 
>> control means that the entire heap will be scanned, regardless of 
>> what occurs.
>>
>> I think better with examples, so say we are counting all of the 
>> instances of classes, by name:
>>
>>
>>   final Foo outside_class = ...;
>>
>>   HeapVisitor visitor = new HeapVisitor() {
>>       HashMap<String,Long> classCount = new HashMap<String,Long>();
>>       Foo inside_class = ...;
>>
>>       void visitObject( JavaObject obj )  {
>>           // Both outside_class and inside_class are visible.
>>           // Var outside_class cannot be modified, of course,
>>           // but fields in the object it refers to may.
>> 1         JavaClass clazz = obj.getJavaClass();
>> 2         String className = clazz.getName();
>>           Long count= classCount.get(className);
>>                  if (count.longValue() == 0) {
>>             classCount.put(className, 1);
>>          } else {
>>             classCount.put(className, count+1);
>>          }
>>       }
>>
>>       void handleDataUnavailable( JavaObject obj ) {
>>           // Same here.
>>           // If we didn't put this method here, the default
>>           // would just throw a DataUnavailableException.
>>       }
>>            void HandleCorruptDataException(JavaObject obj) {
>>       }
>>   };
>>
>>    try{
>>       JavaHeap heap = factory.getJavaHeap(...);
>>    }catch(CorruptDataException e) {
>>    }catch(DataUnavailableException e) {
>>    }
>>   heap.visit( visitor );
>>
>> We could get CorruptDataException at 1 and 2, one would be because of 
>> the JavaObject, the other would be because of the JavaClass it 
>> referred to. I would posit that the exception information should be 
>> sufficient to properly report the problem in most cases as the 
>> processing done in a visitor method would be to do only with the 
>> object it is passed, and anything fairly close to it, such as its 
>> classes and fields.
>>
>> Of course, how well would this work for smaller items, such as fields 
>> in classes, classes in classloaders, etc.
>> With fields, I tend to want to address them by name or get all of 
>> them from a class.
>>
>>
>>
>> Nicholas Sterling wrote:
>>> That last example, the JDBC sanitizer, is much more conventional -- 
>>> there's no handler stack.  It is essentially a combination of the 
>>> Template and Visitor design patterns.  The Template pattern is often 
>>> applied to Java exceptions, so I don't think this would be thought 
>>> of as unusual.
>>>
>>> Instance variables in the anonymous class would be accessible to 
>>> both the method doing the work and the method handling the 
>>> exceptions.  And the class could access final variables outside it:
>>>
>>>    final Foo outside_class = ...;
>>>
>>>    HeapVisitor visitor = new HeapVisitor() {
>>>
>>>        Foo inside_class = ...;
>>>
>>>        void visitObject( JavaObject obj ) {
>>>            // Both outside_class and inside_class are visible.
>>>            // Var outside_class cannot be modified, of course,
>>>            // but fields in the object it refers to may.
>>>        }
>>>
>>>        void handleDataUnavailable( JavaObject obj ) {
>>>            // Same here.
>>>            // If we didn't put this method here, the default
>>>            // would just throw a DataUnavailableException.
>>>        }
>>>    };
>>>
>>>    JavaHeap heap = factory.getJavaHeap(...);
>>>    heap.visit( visitor );
>>>
>>> Does something like that seem reasonable?
>>>
>>> Nicholas
>>>
>>>
>>>
>>> Daniel Julin wrote:
>>>> It seems to me that with all this discussion of polymorphic and 
>>>> stackable
>>>> error handlers, we're rapidly re-inventing much of the Java exception
>>>> facility, and probably rediscovering a lot of the same design
>>>> considerations that the original designers of that Java exception 
>>>> facility
>>>> faced...
>>>>
>>>> As far as I can tell, the one major difference is that, with Java
>>>> exceptions, once an exception is thrown, the flow of control is 
>>>> irrevocably
>>>> interrupted and can only resume after the exception handler, not 
>>>> back at
>>>> the point where the exception was thrown. This means that if you do 
>>>> want
>>>> fine control over your errors but don't want to interrupt a whole 
>>>> sequence
>>>> of operations, you're forced to put an explicit try/catch block at 
>>>> every
>>>> line of the program. Whereas with these proposed handlers, a single
>>>> top-level handler could presumably take care of all the errors in a
>>>> sequence of operations without interrupting that sequence.
>>>>
>>>> Is that a correct interpretation? And is there no practical way to 
>>>> achieve
>>>> the same effect with actual Java exceptions? Some sort of "continue"
>>>> statement inside a catch block, maybe?
>>>>
>>>>
>>>> On the other hand, one thing that Java try/catch blocks offer is 
>>>> controlled
>>>> access to the local variables of the method in which the try/catch 
>>>> block
>>>> resides, and to the private instance members of the object. The 
>>>> proposed
>>>> handlers, since they are executing in a separate object and a separate
>>>> method, would not enjoy a similar access. Whether this limitation 
>>>> would
>>>> prove bothersome in practice, will depend on the actual usage 
>>>> scenarios
>>>> that we face...
>>>>
>>>>
>>>> -- Daniel --
>>>>
>>>>
>>>>
>>>> Nicholas.Sterling@Sun.COM wrote on 2009-04-14 05:39:40 PM:
>>>>  
>>>>> If we were to end up doing something like this, would we use 
>>>>> checked or
>>>>> unchecked exceptions?  If we use checked exceptions, the client will
>>>>> still have to catch the exception or say that the method throws it.
>>>>> Presumably that would defeat the purpose...
>>>>>
>>>>> Good idea on the comparison code.  I'm a little concerned that people
>>>>> may think we are overengineering the whole exception thing; once 
>>>>> we have
>>>>> the comparison code, we can run it by some folks and see whether 
>>>>> their
>>>>> brains short-circuit.
>>>>>
>>>>> It might be helpful as a reference point to consider an example with
>>>>> another API.  I was sufficiently frustrated by the unreadability 
>>>>> of my
>>>>> JDBC clients that I wrote a pair of classes, Query and Querier, that
>>>>> hide gory details, and I think it makes a big difference.  Among the
>>>>> hidden are the binding of variables and iterating through result 
>>>>> sets,
>>>>> but probably the biggest benefit is from hiding the 
>>>>> exception-handling
>>>>> logic (it closes the ResultSet for you on an exception). This is 
>>>>> what it
>>>>> looks like to use it:
>>>>>
>>>>>     // Create a query to get recent bugs.
>>>>>     static final Query recent_bugs_query = new Query(
>>>>>         "bugs submitted against a PRODUCT in the last NUM DAYS",
>>>>>         "select id, synopsis from bugs " +
>>>>>        " where product = ? and date_submitted > sysdate - ?"
>>>>>     );
>>>>>     ...
>>>>>     // Given a Connection conn and values for PRODUCT and NUM DAYS,
>>>>>     // query the DB for recent bugs and display the resulting rows.
>>>>>     new Querier( recent_bugs_query, conn, product, num_days ) {
>>>>>         public void doRow() throws SQLException {
>>>>>             System.out.println( rs.getString(1) + " " + rs.getString
>>>>>     
>>>> (2) );
>>>>  
>>>>>         }
>>>>>     }
>>>>>
>>>>> A major benefit was getting rid of that awful doubly-nested catch 
>>>>> block
>>>>> (closing the ResultSet in the catch block may throw an exception, 
>>>>> so it
>>>>> requires its own try-catch -- gaah!).
>>>>>
>>>>> The default Querier throws an exception, but you can extend 
>>>>> Querier and
>>>>> override the handleException() method to do whatever is 
>>>>> appropriate for
>>>>> your app, and they use your custom Querier throughout your 
>>>>> program, e.g.
>>>>>
>>>>>     class MyQuerier extends Querier {
>>>>>         void handleException( Exception ex ) {
>>>>>             ....
>>>>>         }
>>>>>     }
>>>>>
>>>>> Perhaps we could use a similar approach, for example providing a
>>>>> HeapQuerier class from which clients create anonymous classes to 
>>>>> do what
>>>>> they want.
>>>>>
>>>>> Nicholas
>>>>>
>>>>>
>>>>>
>>>>> Steve Poole wrote:
>>>>>  
>>>>>> On Mon, Apr 13, 2009 at 4:34 AM, Nicholas Sterling <
>>>>>> Nicholas.Sterling@sun.com> wrote:
>>>>>>
>>>>>>
>>>>>>    
>>>>>>> And a Handler (whatever it should really be called) would have

>>>>>>> access
>>>>>>>         
>>>> to
>>>>  
>>>>>>> the previous Handler on the stack, so it could do
>>>>>>>
>>>>>>>   void handleJavaObjectUnavailable(...) {
>>>>>>>       // do some stuff, then defer to the guy beneath us on the

>>>>>>> stack:
>>>>>>>       prevHandler().handleJavaObjectUnavailable(...);
>>>>>>>   }
>>>>>>> Nicholas
>>>>>>>
>>>>>>> This is cool -  The callback approach is sort of a half way
>>>>>>>         
>>>> housebetween a
>>>>  
>>>>>> DOM and SAX model.  It could allow us to have a default "no nulls"
>>>>>>       
>>>> approch
>>>>  
>>>>>> for an implementation but still allows for users of the API to do
>>>>>>       
>>>> something
>>>>  
>>>>>> different.
>>>>>>
>>>>>> I think we should create some comparison code segments to see 
>>>>>> what it
>>>>>>       
>>>> could
>>>>  
>>>>>> look like.
>>>>>>
>>>>>>
>>>>>>    
>>>>>>> Nicholas Sterling wrote:
>>>>>>>
>>>>>>>
>>>>>>>      
>>>>>>>> Daniel Julin wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>>        
>>>>>>>>> I like that approach a lot, because it may also address
the other
>>>>>>>>>             
>>>> concern
>>>>  
>>>>>>>>> that a proposed "default reasonable behavior" may not
be 
>>>>>>>>> appropriate
>>>>>>>>>             
>>>> for
>>>>  
>>>>>>>>> all usage scenarios. We could probably come-up with a
variety of
>>>>>>>>>             
>>>> handlers
>>>>  
>>>>>>>>> for various common behaviors, like printing a simple
error 
>>>>>>>>> message,
>>>>>>>>> completely ignoring the error, and lots of other creative

>>>>>>>>> responses.
>>>>>>>>>
>>>>>>>>> Incidentally, nothing in this discussion is particularly

>>>>>>>>> specific to
>>>>>>>>>             
>>>> the
>>>>  
>>>>>>>>> Kato API, is it? Are we saying that, in general, we don't
like
>>>>>>>>>             
>>>> exceptions
>>>>  
>>>>>>>>> as the standard mechanism to report errors in Java, and
that 
>>>>>>>>> we're
>>>>>>>>> inventing new patterns?  If so, have any useful patterns
been
>>>>>>>>>             
>>>> proposed
>>>>  
>>>>>>>>> and
>>>>>>>>> documented previously in the literature?
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>             
>>>>>>>> I just looked around a little, and am only seeing suggestions

>>>>>>>> for how
>>>>>>>>           
>>>> the
>>>>  
>>>>>>>> *client* can abstract out the exception-handling logic using
the
>>>>>>>>           
>>>> Template
>>>>  
>>>>>>>> design pattern.  So far I haven't seen any advice for API

>>>>>>>> designers.
>>>>>>>>
>>>>>>>> By the way, it occurred to me that the setter can have a

>>>>>>>> generic name
>>>>>>>> because overloading will allow us to have a method for each
>>>>>>>>           
>>>> condition:
>>>>  
>>>>>>>>   factory.setHandler( new DataUnavailableHandler( ... ) {
>>>>>>>>       ...
>>>>>>>>   } );
>>>>>>>>
>>>>>>>> Also, it might make sense to push the handler on a stack
rather
>>>>>>>>           
>>>> replace
>>>>  
>>>>>>>> what is there.  That will allow independent modules to modify
just
>>>>>>>>           
>>>> that
>>>>  
>>>>>>>> behavior they need to and then remove those modifications
when 
>>>>>>>> they
>>>>>>>>           
>>>> are no
>>>>  
>>>>>>>> longer needed.  It also means that we can have just one Handler
>>>>>>>>           
>>>>> () class for
>>>>>  
>>>>>>>> all the handlers, e.g.
>>>>>>>>
>>>>>>>>   // Temporarily override the handling of DataUnavailable
errors.
>>>>>>>>   factory.pushHandler( new Handler( ... ) {
>>>>>>>>       void handleJavaObjectUnavailable(...) {
>>>>>>>>           // handling specific for JavaObjects
>>>>>>>>       }
>>>>>>>>       void handleDataUnavailable(...) {
>>>>>>>>           // handling for all other DataUnavailable conditions
>>>>>>>>       }
>>>>>>>>       // All handler methods not overridden will simply call

>>>>>>>> the same
>>>>>>>> method
>>>>>>>>       // for the object beneath us on the stack.  If we get
to the
>>>>>>>>           
>>>> bottom,
>>>>  
>>>>>>>> the
>>>>>>>>       // handler there will throw an exception.
>>>>>>>>   } );
>>>>>>>>   // Do some work that might cause an exception.  This might

>>>>>>>> include
>>>>>>>> calling
>>>>>>>>   // an independently written module that also wants to 
>>>>>>>> temporarily
>>>>>>>> override
>>>>>>>>   // some handler, but they will pop that before returning
to us.
>>>>>>>>   factory.popHandler();
>>>>>>>>
>>>>>>>> Nicholas
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>        
>>>>>>>>> -- Daniel --,
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Nicholas.Sterling@Sun.COM wrote on 2009-04-11 01:48:53
AM:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>          
>>>>>>>>>> Daniel Julin wrote:
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>            
>>>>>>>>>>> I guess a two mode approach would make everyone
happy. But 
>>>>>>>>>>> would
>>>>>>>>>>>                 
>>>> it
>>>>  
>>>>>>>>>>>
>>>>>>>>>>>                 
>>>>>>>>>> make
>>>>>>>>>>
>>>>>>>>>>               the API too complicated?
>>>>>>>>>>
>>>>>>>>>>            
>>>>>>>>>>>
>>>>>>>>>>>                 
>>>>>>>>>> I have some sympathy for what Steve is talking about
-- maybe my
>>>>>>>>>> short-term memory is small, but when lots of single
lines of 
>>>>>>>>>> code
>>>>>>>>>> inflate to 6 lines (and two indentation levels),
it is 
>>>>>>>>>> definitely
>>>>>>>>>>               
>>>> harder
>>>>  
>>>>>>>>>> for me to read.  However, I wouldn't want to give
up the certain
>>>>>>>>>>               
>>>> and
>>>>  
>>>>>>>>>> immediate catching of errors offered by exceptions.
>>>>>>>>>>
>>>>>>>>>> Would a mechanism like this work for the two-mode
approach?
>>>>>>>>>>
>>>>>>>>>>    factory.setDataUnavailableHandler( new DataUnavailableHandler
>>>>>>>>>>               
>>>> ( ... )
>>>>  
>>>>>>>>>>
>>>>>>>>>>               
>>>>>>>>> {
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>          
>>>>>>>>>>        ...
>>>>>>>>>>    } );
>>>>>>>>>>
>>>>>>>>>> All objects created by the factory would call that
object's
>>>>>>>>>> dataUnavailable() method when appropriate, passing
it enough 
>>>>>>>>>> info
>>>>>>>>>>               
>>>> about
>>>>  
>>>>>>>>>> what was going on to allow the method to make interesting

>>>>>>>>>> decisions
>>>>>>>>>> about what to do.  The default handler would always
throw a
>>>>>>>>>> DataUnavailableException.
>>>>>>>>>>
>>>>>>>>>> It's hard for me to tell whether something like that
would 
>>>>>>>>>> really
>>>>>>>>>> suffice in actual use.  Perhaps it would have to
be 
>>>>>>>>>> finer-grained,
>>>>>>>>>>               
>>>> with
>>>>  
>>>>>>>>>> methods for javaObjectUnavailable(), imageSectionUnavailable(),
>>>>>>>>>>               
>>>> etc.
>>>>  
>>>>>>>>>> Perhaps the defaults for those would call the more
generic
>>>>>>>>>> dataUnavailable() so that you could intervene for
all cases 
>>>>>>>>>> and/or
>>>>>>>>>>               
>>>> for
>>>>  
>>>>>>>>>> individual cases as desired.
>>>>>>>>>>
>>>>>>>>>> Nicholas
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>               
>>>>>>>>>             
>>>>>>       
>>>>
>>>>   
>>>

Mime
View raw message