Community
Participate
Working Groups
This code compiles properly under AspectJ 1.0.6, but it fails under 1.1.0 and under cvs head as of 9/8/03: C:\eclipse\workspace\atrack>ajc src\SampleExceptionHandling.java C:\eclipse\workspace\atrack\src\SampleExceptionHandling.java:3 exact type patter n required 1 error // Copyright (c) 2003 New Aspects of Security. All Rights Reserved. public aspect SampleExceptionHandling { declare soft: (Exception+ && !RuntimeException+): within(com.example..*); }
contributing the source per CPL: /** @author Ron Bodkin */ public aspect SampleExceptionHandling { declare soft: (Exception+ && !RuntimeException+): within(com.example..*); }
This is a change that was made for the 1.1.0 implementation and documented in the release notes, i.e. 'declare soft: TYPE: POINTCUT;' - AspectJ 1.1 only accepts TYPE rather than a TYPE_PATTERN. This limitation makes declare soft much easier to implement efficiently. (CTI) This issue is more serious than it sounds from the brief description in the release notes and any proposal to expand declare soft to take a TYPE_PATTERN again in 1.2 will have to include a compiler patch demonstrating how it might be implemented. Personally, I think that the main question here is how unchecked exceptions should interact with declare soft. Maybe we need a different form of declare soft that won't ever wrap unchecked exceptions... I'm leaving this as a P4 enhancement request to be rediscussed for 1.2.
I agree, the major issue that I want to address is only converting checked exceptions, not RuntimeExceptions. This _can_ be done, albeit less efficiently, by recovering the original RuntimeException in around advice that catches the SoftException. Another enhancement that would fix this and other useful idioms would be supporting defined type patterns, rather like declare parents except this would be tracked only at compile time, but could also include classes that the compiler doesn't control. I realize that this would be a major crosscutting change. I did look at the release notes, but stopped reading after the list of changes for bytecode weaving (obviously I skimmed too fast). Interestingly the compiler accepts Exception+ silently; using a pattern like that is worth a warning.
I just want to note that this issue was not fixed in AspectJ 1.2, and still severely limits the usefulness of declare soft. I don't think that it's necessary that declare soft accept a type pattern. I would be comfortable if it implicitly softened only checked exceptions. As Ron notes, there's a workaround. However, it shouldn't be necessary to use a workaround for the most generic use case of a language feature. Is there any chance this issue can make it into a point release of AspectJ 1.2? AFAICT changing the behavior of the feature should have minimal impact on existing code. The only problem I can forsee would be client code expecting (i.e. catching) SoftException that was supposed to have wrapped a RuntimeException.
re-opening the debate on this for 1.2.1.
Is there any debate to be had? Or are you just reopening with a target of 1.2.1?
Yes, I think there is - or at least some education on my part. The change I'm prepared to consider is making declare soft NOT soften runtime exceptions, but I want to really understand the strong use cases for this before we do it. The reason that this needs to be carefully justified is that it changes the semantics of the language (and in a point release) in a way that may produce unexpected runtime failures (much worse than getting new compilation errors or warnings on upgrading). The nightmare scenario is someone using declare soft to deliberately soften runtime exceptions, and then catching a SoftException somewhere and expecting it to handle *all* cases. If we make the change, this program will still compile happily, but an NPE say would slip under the radar of their exception handling. The counter-argument is that we are adding new aspectj users all the time, so if we think this is the right thing to do, it is better to do it sooner rather than later. So, I'd like to understand why you both believe that 'the usefulness of declare soft is severely limited' as it is. I always imagined declare soft being used in conjunction with advice. So you write some advice that deals with an exception condition at a given set of join points (after throwing for mapping into domain exceptions, around advice if you are genuinely going to handle), and once that advice is in place you say to the compiler "OK, I've got an exception handling policy in place, so please don't complain about needing to handle checked exceptions at these join points". I'm sure I recall from earlier discussions on this topic that this was Jim's opinion too. When used like this, there won't be any logic in your application that is working with SoftExceptions at all. (And yes, when you read that sentence it seems logical that declare soft would not soften unchecked exceptions - but it does today, so we need a strong enough case to change the status quo). When you use declare soft in conjunction with advice then, that just leaves the case where you write something like: after() throwing(Exception ex) : pc() { if (ex instanceof RuntimeException) { throw ex; } else { throw new DomainException(ex); } } The nuisance here is that the RuntimeException you wanted to throw from the advice will get softened. BUT - is this a useful use case that justifies the change? If you're mapping to domain exceptions, why would you not also convert runtime exceptions? And if you don't want runtime exceptions, why ask for them in the advice spec.? For example, if you write: try {...} catch(Exception ex) {...} you'd better be prepared to handle what comes your way. Likewise if you write after throwing or declare soft against just "exception". It's a much better practice to catch the exceptions you actually want isn't it?? Another reason not to change is that the semantics are currently 'instanceof' (declare soft softens any instanceof the given exception) which is very clear and already well-understood by Java programmers. So, I've set before the jury the case for leaving the implementation as-is. Can we now hear the evidence in favour of the change? ;)
Ron, Nick - a last call before I shut the door on this. I made the case for *not* implementing this change in my last append, and am waiting for you to make a stronger case in favor of the change. If there isn't one, then I propose to close out the enhancement request, but if there is, I'm still prepared to listen...
I do think this change is quite important. One of the biggest issues I have with the current approach is that it doesn't let me implement a policy of handling an exception once by converting it to an unchecked domain exception efficiently or modularly. I.e., I am of the school of thought that domain exceptions should be unchecked, so their handling isn't scattered and tangled across layers of code that don't need to be involved in their handling. Consider the aTrack code for exception handling below. Because we want to soften any exception that internal methods throw and convert to domain exceptions, we are forced to keep softening and re-converting them at every method until the exception is eventually handled. This type of exception policy is quite popular and useful (I've seen applications where it was literally implemented manually with try-catch in every method). So I think the value of supporting it far outweighs the cost of making a small backward compatibility change. If you want AJC 1.0.6 behavior you just use: after() throwing (RuntimeException e) : somePointcut() { throw new SoftException(e); } Sample code follows: I'd venture to say that as it's currently implemented, declare soft is only usable for narrowly defined categories of exceptions with a common base type. I think that's a shame; there are definitely cases you'd like to public abstract aspect ExceptionHandling { ... private pointcut exceptionConversionPoint(): (StandardPointcuts.anyExec() || staticinitialization(*)) && inScope(); /** define where to soften checked exceptions */ //we really need a declare soft for !RuntimeException ... //we do NOT want to keep rethrowing SoftExceptions declare soft: Exception: exceptionConversionPoint(); after() throwing(Exception e) : exceptionConversionPoint() { Throwable t = e; while (t instanceof SoftException) { t = ((SoftException)t).getWrappedThrowable(); } if (!(t instanceof AtrackException)) { Log log = getLogger(); if (log.isWarnEnabled()) { log.warn("converted exception at "+thisJoinPointStaticPart, e); log.warn(AtrackExecutionTracer.aspectOf().getJPContextStr(thisJoinPoint)); } throw convert(e); } else { throw (AtrackException)t; } } protected RuntimeException convert(Throwable t) { ... } } seen with the current behavior is exactly the problem of softening checked exceptions. If one has a policy that
I should have said AJC 1.2 and earlier code, not AJC 1.0.6. Let me know if you want references to some articles that argue for using unchecked exceptions for applications. In practice, everyone in the Java world makes these extend RuntimeException and _not_ Error, so it would be a shame to not support policies that soften checked exceptions and convert them to user-defined subclasses of RuntimeException. Long term, I'd like to see a declare throws form for AspectJ, which would allow using checked exceptions for interface methods without scattering and tangling them throughout the system.
I implemented this change on the plane on the way across to Chicago. declare soft now no longer softens RuntimeExceptions. The change is described in the AJDK Developer's Notebook under the 'miscellaneous' heading: http://dev.eclipse.org/viewcvs/index.cgi/org.aspectj/modules/docs/adk15ProgGuideDB/miscellaneous.xml?rev=HEAD&content-type=text/vnd.viewcvs-markup&cvsroot=Technology_Project Fix now in CVS HEAD. Will close this bug report once the fix is available in a published build.
Should have been closed off as fixed a long time ago.....