在Java中对方法体施加约束或限制 上下文(编辑)

在Java中对方法体施加约束或限制 上下文(编辑),java,architecture,methods,constraints,invariants,Java,Architecture,Methods,Constraints,Invariants,一些澄清是按要求进行的,所以我将尝试总结一下是什么影响了这个问题 该项目的目标是为程序员提供某种功能,最有可能的形式是一个库(我想是一个带有类文件的JAR) 要使用上述功能,程序员必须遵守必须(应该)满足的约束。否则它将无法正常工作(就像java.util.concurrent中的锁一样,必须在适当的时间和地点获取/释放这些锁) 此代码不会成为使用它的应用程序的入口点(即,没有main) API中公开的操作数量有限(而且很少) 示例: 想想一个小游戏,几乎所有的东西都是由已经实现的类实现和管

一些澄清是按要求进行的,所以我将尝试总结一下是什么影响了这个问题

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

  • 要使用上述功能,程序员必须遵守必须(应该)满足的约束。否则它将无法正常工作(就像
    java.util.concurrent
    中的锁一样,必须在适当的时间和地点获取/释放这些锁)

  • 此代码不会成为使用它的应用程序的入口点(即,没有
    main

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

示例:

  • 想想一个小游戏,几乎所有的东西都是由已经实现的类实现和管理的。程序员唯一要做的就是编写一个或两个方法来描述角色将要做的事情(行走、改变方向、停止、检查对象)。我希望确保他们的方法(可能用注释标记?)只是
    行走
    ,或者
    更改方向
    ,或者计算
    diff=desiredValue-x
    ,而不是写入某个文件,或者打开套接字连接

  • 想象一下事务管理器。该库将提供管理器,以及事务的一些常量属性(它们的隔离级别、超时等)。现在,程序员希望拥有事务并使用这个管理器。我希望确保他们只对经理已知的某些资源执行
    读取
    写入
    提交
    、或
    回滚
    。我不想让他们在交易过程中<代码>发射火箭< /代码>,如果管理者不控制任何火箭发射。

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

    public abstract class ToBeExtended {
        // some private stuff they should not modify
        // ...
        public abstract SomeReturnType safeMethod();
    }
    
    对于这个项目来说,方法体满足一些不变量是很重要的(可能是必须的)。或者更确切地说,该方法实现使用的命令集必须是有限的。这些限制的示例:

    • 此方法不得执行任何I/O
    • 此方法不能实例化任何未知(潜在危险)对象
    换句话说:

    • 此方法可以调用已知(特定)类的方法
    • 此方法可以执行一些基本指令(数学、分配局部变量、
      if
      s、循环…)
    我一直在浏览注释,似乎没有什么与此类似的内容。
    我目前的选择:

  • 定义一些注释,
    @SafeAnnotation
    ,并将其应用于方法,定义与实施者的契约,以确保他将遵守强加的规则,否则系统将出现故障

  • 使用允许的操作定义一个
    Enum
    。不是公开允许的方法,而是只公开一个方法,该方法接受这些枚举对象的列表(或类似于枚举对象的东西)并执行它,让我控制可以做什么

  • 例如:

    public enum AllowedOperations { OP1, OP2 }
    
    public class TheOneKnown {
        public void executeMyStuff (List<AllowedOperations> ops) {
            // ...
        }
    }
    
    公共枚举允许的操作{OP1,OP2}
    公共类TheOneKnown{
    public void executeMyStuff(列表)。尽管如此,这可能需要重新考虑体系结构


    使用注释来限制的整个想法似乎需要实现我自己的注释处理器。如果是这样的话,我不妨考虑一个小的域特定语言,这样程序员就可以使用这些有限的操作,然后把代码翻译成java。这样,我也可以控制什么是指定的。d、

    Java有几个可用的库,但我不能特别推荐一个。这似乎是一个轻量级的解决方案,但再一次,我没有使用它的第一手经验。

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

    这里有一些关于政策文件的文档


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

    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.";
        }
    }
    
    当然,这需要注意白名单中的类。它也不能防止拒绝服务,例如

    while (true) {}
    


    另一种选择是使用en嵌入式脚本解释器,例如groovy one(),并在运行时通过预执行验证来评估第三方方法的内容

    优点是,您可以将访问权限限制为仅限于将为脚本执行绑定的变量


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

    我认为方向是好的

    • 使用特定的
      ClassLoader
      加载类。注意,它们是一种有趣的类型,通常情况下,类本身由父类加载器加载。可能您需要某种类型的类加载器,而父类加载器将设置为根类加载器,但这是不够的
    • 使用
      threads
      避免无限循环(而不是实现
      Runnable
      而不是像这样扩展
      Thread
      ),如果您不担心的话,这可能是不必要的
    • 使用SecurityManager避免
      java.io
      操作
    除上述内容外,我推荐两个选项:

    为该方法提供一个控制器,这将
    while (true) {}
    
    new long[1000000000];
    
    public void foo(Controller ctrl) {
    }
    
    public class Controller {
       public boolean commit();
       public boolean rollback();
    }