incubator-kato-spec mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Nicholas Sterling <Nicholas.Sterl...@Sun.COM>
Subject Re: Kato API javadoc - error handling
Date Wed, 15 Apr 2009 16:27:20 GMT
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