Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Imposing constraints or restrictions on method body, in Java

Context (Edit)

Some clarification was on demand, so I'll try to sum up what influences the question.

  • The goal of the project is to provide a certain functionality to programmers, most probably in the form of a library (a JAR with class files, I guess).

  • To use said functionality, programmers would have to conform to the constraints that must (should) be satisfied. Otherwise it won't function as expected (just like the locks from java.util.concurrent, that must be acquired / freed at the appropriate time and place).

  • This code won't be the entry point to the applications using it (ie, has no main).

  • There's a limited (and small) amount of operations exposed in the API.

Examples:

  1. Think of a small game, where almost everything is implemented and managed by the already implemented classes. The only thing left for the programmer to do, is to write a method, or a couple of them, that describe what the character will do (walk, change direction, stop, inspect object). I would want to make sure that their methods (possibly marked with an annotation?) just walk, or changeDirection, or calculate diff = desiredValue - x, and not, say, write to some file, or open a socket connection.

  2. Think of a transaction manager. The manager would be provided by this library, as well as some constant attributes of transactions (their isolation level, time-outs, ...). Now, the programmers would like to have transactions and use this manager. I would want to make sure that they only read, write, commit, or rollback on some resources, known to the manager. I wouldn't want them to launchRocket in the middle of the transaction, if the manager does not control any rockets to launch.

The Problem

I want to impose some invariants / restrictions / constraints on the body of a method (or group of methods), to be later implemented by some other programmer, in some other package/location. Say, I give them something along the lines of:

public abstract class ToBeExtended {
    // some private stuff they should not modify
    // ...
    public abstract SomeReturnType safeMethod();
}

It is important (probably imperative), for the purposes of this project, that the method body satisfies some invariants. Or rather, it is imperative that the set of commands this method's implementation uses is limited. Examples of these constraints:

  • This method must not perform any I/O.
  • This method must not instantiate any unknown (potentially dangerous) objects.
  • ...

Put another way:

  • This method can call the methods of a known (specific) class.
  • This method can execute some basic instructions (maths, assign local variables, ifs, loops...).

I've been looking through Annotations, and there seems to be nothing close to this.
My options so far:

  1. Define some annotation, @SafeAnnotation, and apply it to the method, defining a contract with the implementer, that he will follow the rules imposed, or else the system will malfunction.

  2. Define an Enum with the allowed operations. Instead of exposing the allowed methods, only a method is exposed, that accepts a list of these enum objects (or something similar to a Control Flow Graph?) and executes it, giving me the control of what can be done.

Example:

public enum AllowedOperations { OP1, OP2 }

public class TheOneKnown {
    public void executeMyStuff (List<AllowedOperations> ops) {
        // ...
    }
}

My Question

Is there any feature in the language, such as annotations, reflection, or otherwise, allowing me to inspect (either at compile time or runtime) if a method is valid (ie, satisfies my constraints)?
Or rather, is there any way to enforce it to call only a limited set of other methods?

If not (and I think not), would this second approach be a suitable alternative?
Suitable, as in intuitive, well designed and/or good practice.

Update (Progress)

Having had a look at some related questions, I'm also considering (as a third option, maybe) following the steps given in the accepted answer of this question. Although, this may require some rethinking on the architecture.

The whole idea of using annotations to impose restrictions seems to require implementing my own annotation processor. If this is true, I might as well consider a small domain-specific language, so that the programmer would use these limited operations, later translating the code to Java. This way, I would also have control over what is specified.

like image 639
afsantos Avatar asked Apr 11 '13 21:04

afsantos


2 Answers

Have a look at java policy files. I've not used them, and I'm not sure they'll fit your problem exactly, but with some digging into the docs they may be a fit. Here's a couple SO questions that may be of help

Limiting file access in Java

What is a simple Java security policy for restricting file writes to a single directory?

And here's some documentation on the policy file.

http://docs.oracle.com/javase/6/docs/technotes/guides/security/PolicyFiles.html

like image 125
digitaljoel Avatar answered Nov 15 '22 17:11

digitaljoel


I think that the direction in this question is good.

  • Use a specific ClassLoader lo load the class. Beware, that they're an interesting type of horse, it usually happens that the class itself is loaded by a parent classloader. Probably you want some sort of UrlClassLoader, and the parent classloader would be set to the Root classloader It is not enough, though.
  • Use threads to avoid infinite loops (rather implementing Runnable than extending Thread, like there) - this may be unnecessary if you're not worrying about it.
  • Use SecurityManager to avoid java.io operations

In addition to the above, I recommend 2 options:

Give the method a controller, that would contain the functions it can call

For example:

public void foo(Controller ctrl) {
}

public class Controller {
   public boolean commit();
   public boolean rollback();
}

This can give the user a handle, what operations are allowed.

Use an Intent-like command pattern

In Android, the components of the system are quite closed. They cannot directly communicate to each other, they only can fire an event, that "this happened", or "I want to do that".

This way the set of the usable commands are not restricted. Usually, if the methods do only small business logic, that is enough.

like image 27
gaborsch Avatar answered Nov 15 '22 18:11

gaborsch