Reputation: 9212
In short: I want to allow/forbid the execution of Java methods depending on certain conditions. What is the best available solution/mechanism/technique?
Long question, via an (admittedly dumb) example:
Say I have several methods in a vehicle controller, like void openWindow(Window w)
, void openRoof()
, void keepSpeed(double speedKmh)
. If it rains, the openRoof
should not be called, and we want to put in place a mechanism to ensure it. Similarly, if the speed is below 60 km/h, keepSpeed
is forbidden, like openWindow
if it rains a lot or if the speed if above 100 km/h
.
Since accessing the rain/speed sensors requires several lines of code, and these conditions are used everywhere, I don't want to use use assertions or conditions in the body of the methods, but I'd rather make them easily used by domain developers. Besides, I'd like the security concern to be separated from the actual logic of opening the windows, etc. More complex, custom conditions should be easy to configure too.
For instance, I'd like to have this:
@ForbidIf('default:rain') openWindow();
@ForbidIf('default:speedBelow(60)') keepSpeed();
@ForbidIf('default:speedAbove(100)', 'custom:rainsALot') openWindow();
If it helps, this application is a Spring-powered client-server application.
Upvotes: 2
Views: 367
Reputation: 298828
You can use a simple Spring AOP aspect, something like this (untested):
@Aspect
public class SecurityAspect{
@Pointcut("execution(@ForbidIf * *(*))")
public void annotatedMethodCalled(){}
@Before("annotatedMethodCalled() && @target(annotation) && target(bean)")
public void beforeRestrictedMethodCall(
final ForbidIf annotation, final Object bean){
final Expression expression =
new SpelExpressionParser().parseExpression(annotation.value());
if(!Boolean.TRUE.equals(expression.getValue(bean))){
throw new IllegalArgumentException();
}
}
}
Upvotes: 2
Reputation:
Do you need AOP here? the Decorator pattern can be a lot simpler and requires a lot less magic. Just wrap the implementation with:
class ProtectedWindowOpener implements WindowOpener {
WindowOpener delegate ...
void openWindow(Window w) {
if (!allowOpen(w)) {
throw new PermissionDeniedException...
}
delegate.openWindow(w);
}
boolean allowOpen(Window w) {
// security logic here
}
}
Which keeps the security logic separate and also has the advantage of not embedding code in a string, which means eclipse can do its business.
Upvotes: 0
Reputation: 3169
OP, commenting to Andreas_D's answer: "If the execution is forbidden, I want a runtime exception to be raised. In this case, if the condition is false, it means this method should have never been called under the current circumstances."
What's wrong with:
public void openWindow() {
if (itsRaining()) {
throw new IllegalStateException("Window should not open if it's raining");
}
}
? I mean, doing an annotation is effectively the same thing, only more complex (especially if your conditions for the method grow in complexity, for example if the window should not be opened if it's raining, snowing, vehicle speed > 100 KPh, wind speed > 6, temperature below freezing, etc).
Of course, I could also just be missing the point. Just wanted to give the hint that overthinking, -engineering or -complicating the problem aren't the only paths to take. Don't want to say that using AOP or annotations are overthinking/engineering/complication a problem, of course. Probably basing this post on the simplified example, too.
Upvotes: 1
Reputation: 242686
Something similar is implemented in Spring Security as expression based access control, but I think it's not suitable in your case.
However, it should be easy to implement similar functionality from scratch by creating an aspect that can evaluate SpEL expressions against a certain context.
Upvotes: 1
Reputation: 114757
This is an interesting idea, although I see some real problems. You may annotate a method and refer to some environment settings. But if execution is forbidden, what should be done instead?
Consider this example:
@ForbidIf('default:durationIs(0))') double getSpeed(double distance);
and use it like
double speed = getSpeed(distance); // the duration is set globally in this example
What should happen if the duration value is set to 0 - what should be assigned to speed in that case? Or do you want to raise a runtime exception?
In your case, we already have two ways to implement conditional execution:
// check before calling
if (isSaveToOpenWindow())
openWindow();
and
public void openWindow() {
if (!isSaveToOpenWindow())
return;
// open window
}
Upvotes: 0