上下文(编辑)
有些澄清是需要的,所以我会试着总结影响这个问题的因素。
该项目的目标是为程序员提供某种功能,最有可能是以库的形式(我猜是带有类文件的JAR)。
要使用所述功能,程序员必须遵守这些约束 必须 (应该)满意。否则它将无法按预期运行(就像来自的锁一样) java.util.concurrent
,必须在适当的时间和地点获得/释放)。
此代码不是使用它的应用程序的入口点(即, 没有 main
)。
API中公开的操作数量有限(且很小)。
例子:
想想一个小游戏,几乎所有东西都是由已经实现的类实现和管理的。程序员唯一要做的就是编写一个方法或其中几个描述角色将要做什么的方法(走路,改变方向,停止,检查对象)。我想确保他们的方法(可能标有注释?) walk
, 要么 changeDirection
,或计算 diff = desiredValue - x
,而不是说,写入某个文件,或打开套接字连接。
想想一个交易经理。该库提供管理器,以及事务的一些常量属性(它们的隔离级别,超时,......)。现在,程序员想要进行交易并使用这个经理。我想确保他们只是 read
, write
, commit
, 要么 rollback
在一些资源上,经理知道。我不希望他们这样做 launchRocket
在交易过程中,如果经理不控制任何火箭发射。
问题
我想在方法(或方法组)的主体上施加一些不变量/限制/约束,稍后由其他程序员在其他一些包/位置中实现。说,我给他们一些东西:
public abstract class ToBeExtended {
// some private stuff they should not modify
// ...
public abstract SomeReturnType safeMethod();
}
为了本项目的目的,方法体满足一些不变量是很重要的(可能是必要的)。或者更确切地说,此方法实现使用的命令集是有限的。这些约束的例子:
- 此方法不得执行任何I / O.
- 此方法不得实例化任何未知(有潜在危险)对象。
- ...
换一种方式:
- 此方法可以调用已知(特定)类的方法。
- 这个方法可以执行一些 基本 指令(数学,分配局部变量,
if
s,循环...)。
我一直在浏览Annotations,似乎没有什么可以接近的。
我到目前为止的选择:
定义一些注释, @SafeAnnotation
,并将其应用于方法,与实施者定义合同,他将遵守规则,否则系统将发生故障。
定义一个 Enum
允许的操作。而不是暴露允许的方法,只暴露一个方法,接受这些枚举对象的列表(或类似于 控制流程图?)并执行它,让我控制可以做什么。
例:
public enum AllowedOperations { OP1, OP2 }
public class TheOneKnown {
public void executeMyStuff (List<AllowedOperations> ops) {
// ...
}
}
我的问题
是否有语言中的任何功能,例如注释,反射或其他,允许我检查(在编译时或运行时)方法是否有效(即,满足我的约束)?
或者更确切地说,有没有办法强制它只调用一组有限的其他方法?
如果不是(我认为不是),第二种方法是否合适?
适用于直观,精心设计和/或良好实践。
更新(进度)
看了一些相关的问题后,我也在考虑(作为第三种选择,也许)按照接受的答案给出的步骤 这个问题。虽然,这可能需要对架构进行一些重新思考。
使用注释来施加限制的整个想法似乎需要实现我自己的注释处理器。如果这是真的,我不妨考虑一个特定于域的小语言,以便程序员使用这些有限的操作,然后将代码转换为Java。这样,我也可以控制指定的内容。
我认为是方向 在这个问题上 很好。
- 使用具体的
ClassLoader
加载课程。要注意,它们是一种有趣的马类型,通常会发生类本身由父类加载器加载。可能你想要某种东西 URLClassLoader的,并且父类加载器将被设置为Root类加载器但是这还不够。
- 使用
threads
避免无限循环(而不是实现 Runnable
比延伸 Thread
,就像那里) - 如果你不担心它,这可能是不必要的。
- 使用SecurityManager来避免
java.io
操作
除了以上,我建议2个选项:
给出方法a 调节器,它将包含它可以调用的函数
例如:
public void foo(Controller ctrl) {
}
public class Controller {
public boolean commit();
public boolean rollback();
}
这可以为用户提供句柄,允许哪些操作。
用一个 Intent
类似命令模式
在Android中,系统的组件非常封闭。他们不能直接相互沟通,他们只能发动事件,“发生这种情况”或“我想这样做”。
这样,可用命令的集合不受限制。通常,如果方法只做小业务逻辑,那就足够了。
您可以使用自定义类加载器限制不受信任的代码使用的类:
public class SafeClassLoader extends ClassLoader {
Set<String> safe = new HashSet<>();
{
String[] s = {
"java.lang.Object",
"java.lang.String",
"java.lang.Integer"
};
safe.addAll(Arrays.asList(s));
}
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
if (safe.contains(name)) {
return super.loadClass(name, resolve);
} else {
throw new ClassNotFoundException(name);
}
}
}
public class Sandboxer {
public static void main(String[] args) throws Exception {
File f = new File("bin/");
URL[] urls = {f.toURI().toURL()};
ClassLoader loader = new URLClassLoader(urls, new SafeClassLoader());
Class<?> good = loader.loadClass("tools.sandbox.Good");
System.out.println(good.newInstance().toString());
Class<?> evil = loader.loadClass("tools.sandbox.Evil");
System.out.println(evil.newInstance().toString());
}
}
public class Good {
@Override
public String toString() {
return "I am good";
}
}
public class Evil {
@Override
public String toString() {
new Thread().start();
return "I am evil.";
}
}
运行此将导致
I am good
Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/Thread
at tools.sandbox.Evil.toString(Evil.java:7)
at tools.sandbox.Sandboxer.main(Sandboxer.java:18)
Caused by: java.lang.ClassNotFoundException: java.lang.Thread
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
... 2 more
当然,这假设要关注您的白名单。它也无法阻止拒绝服务等内容
while (true) {}
要么
new long[1000000000];
另一种选择是使用en嵌入式脚本解释器,例如groovy(http://groovy.codehaus.org/Embedding+Groovy并使用预执行验证在运行时评估第三方方法内容。
优点是您将能够仅限制对将为脚本执行绑定的变量的访问权限。
您也可以编写自己的验证dsl并将其应用于执行脚本的方法,例如使用自定义注释。
有几个 按合同设计 可用于Java的库,但我不能特别推荐它。 Java参数验证 似乎是一个轻量级的解决方案,但同样,我没有第一手经验。