在Java中对方法体施加约束或限制

时间:2013-04-11 21:21:13

标签: java architecture methods constraints invariants

上下文(编辑)

有些澄清是需要的,所以我会试着总结影响这个问题的原因。

  • 该项目的目标是为程序员提供某种功能,最有可能是以库的形式(我猜是带有类文件的JAR)。

  • 要使用所述功能,程序员必须遵守必须(应该)满足的约束。否则它将无法按预期运行(就像来自java.util.concurrent的锁定一样,必须在适当的时间和地点获取/释放)。

  • 此代码不是使用它的应用程序的入口点(,没有main)。

  • API中公开的操作数量有限(且很少)。

示例:

  1. 想想一个小游戏,几乎所有东西都是由已经实现的类实现和管理的。程序员唯一要做的就是编写一个方法或其中几个描述角色将要做什么的方法(走路,改变方向,停止,检查对象)。我想确保他们的方法(可能标有注释?)只是walk,或changeDirection,或计算diff = desiredValue - x,而不是,比方说,写入某个文件,或者打开套接字连接。

  2. 想想一下交易经理。该库将提供管理器,以及事务的一些常量属性(它们的隔离级别,超时,......)。现在,程序员想要进行交易并使用这个经理。我想确保他们在管理员已知的某些资源上只有readwritecommitrollback。如果经理不控制任何火箭发射,我不希望他们在交易过程中launchRocket

  3. 问题

    我想在方法(或方法组)的主体上施加一些不变量/限制/约束,稍后由其他程序员在其他一些包/位置中实现。说,我给他们一些东西:

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

    对于这个项目来说,重要的(可能是必要的)方法体满足一些不变量。或者更确切地说,此方法实现使用的命令集是有限的。这些约束的例子:

    • 此方法不得执行任何I / O.
    • 此方法不得实例化任何未知(潜在危险的)对象。
    • ...

    换句话说:

    • 此方法可以调用已知(特定)类的方法。
    • 此方法可以执行一些基本指令(数学,分配局部变量,if,循环......)。

    我一直在浏览Annotations,似乎没有任何接近这一点 我到目前为止的选择:

    1. 定义一些注释@SafeAnnotation,并将其应用于方法,与实施者定义合同,他将遵循规则,否则系统将发生故障。

      < / LI>
    2. 使用允许的操作定义Enum。而不是暴露允许的方法,只暴露一个方法,接受这些枚举对象的列表(或类似于Control Flow Graph?)并执行它,让我控制可以做什么。

    3. 示例:

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

      我的问题

      语言中是否有任何功能,例如注释,反射或其他,允许我检查(在编译时或运行时)方法是否有效(,满足我的约束) )?
      或者更确切地说,有没有办法强制它只调用一组有限的其他方法?

      如果不是(我认为不是),第二种方法是否适合作为替代方案? 适用于直观,精心设计和/或良好实践。

      更新(进度)

      看了一些相关的问题后,我也在考虑(作为第三种选择,也许)按照this question的公认答案中给出的步骤。虽然,这可能需要对架构进行一些重新思考。

      使用注释施加限制的整个想法似乎需要实现我自己的注释处理器。如果这是真的,我不妨考虑一个特定于域的小语言,以便程序员使用这些有限的操作,然后将代码转换为Java。这样,我也可以控制指定的内容。

5 个答案:

答案 0 :(得分:6)

查看java策略文件。我没有使用它们,我不确定它们是否完全符合您的问题,但是通过深入研究文档它们可能是合适的。这里有几个可能有帮助的问题

Limiting file access in Java

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

这是关于政策文件的一些文档。

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

答案 1 :(得分:4)

您可以使用自定义类加载器限制不受信任代码使用的类:

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];

答案 2 :(得分:3)

我认为方向in this question很好。

  • 使用特定的ClassLoader lo加载该类。要注意,它们是一种有趣的马类型,通常会发生类本身由父类加载器加载。可能你想要某种UrlClassLoader,而父类加载器将设置为Root类加载器但这还不够。
  • 使用threads来避免无限循环(而不是实现Runnable而不是扩展Thread,就像那里一样) - 如果你不担心它,这可能是不必要的。
  • 使用SecurityManager来避免java.io操作

除上述内容外,我还推荐2个选项:

为方法提供控制器,其中包含可以调用的函数

例如:

public void foo(Controller ctrl) {
}

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

这可以为用户提供句柄,允许哪些操作。

使用类似Intent的命令模式

在Android中,系统的组件非常封闭。他们不能直接相互沟通,他们只能发动事件,“发生这种情况”,或“我想这样做”。

这样一组可用命令不受限制。通常,如果方法只做小业务逻辑,那就足够了。

答案 3 :(得分:2)

另一个替代方案是使用en嵌入式脚本解释器,例如groovy one(http://groovy.codehaus.org/Embedding+Groovy),并使用预执行验证在运行时评估第三方方法内容。

优点是您可以限制只访问要为脚本执行绑定的变量。

您也可以编写自己的验证dsl并将其应用于执行脚本的方法,例如使用自定义注释。

答案 4 :(得分:0)

有几个design by contract库可用于Java,但我不能特别推荐它们。 Java Argument Validation似乎是一个轻量级解决方案,但同样,我没有第一手经验。