Наложение ограничений или ограничений на тело метода, в Java

Контекст (Изменить)

Некоторые разъяснения были по требованию, поэтому я постараюсь подвести итог тому, что влияет на вопрос.

  • Цель проекта - предоставить определенную функциональность программистам, скорее всего, в виде библиотеки (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();
}

Для целей этого проекта важно (возможно, обязательное), чтобы тело метода удовлетворяло некоторым инвариантам. Вернее, крайне важно, чтобы набор команд, используемых в этом методе, ограничен. Примеры этих ограничений:

  • Этот метод не должен выполнять никаких операций ввода-вывода.
  • Этот метод не должен создавать какие-либо неизвестные (потенциально опасные) объекты.
  • ...

Поставьте другой способ:

  • Этот метод может вызывать методы известного (определенного) класса.
  • Этот метод может выполнять некоторые основные инструкции (математика, назначение локальных переменных, if s, циклы...).

Я просматривал аннотации, и, похоже, ничего близкого этому не было.
До сих пор мои варианты:

  • Определите некоторую аннотацию @SafeAnnotation и примените ее к методу, определяя контракт с разработчиком, что он будет следовать установленным правилам, иначе система будет работать неправильно.

  • Определите Enum с разрешенными операциями. Вместо раскрытия разрешенных методов раскрывается только метод, который принимает список этих объектов перечисления (или что-то похожее на Control Flow Graph?) и выполняет его, давая мне контроль над тем, что можно сделать.

Пример:

public enum AllowedOperations { OP1, OP2 }

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

Мой вопрос

Есть ли какая-либо функция на языке, например аннотации, отражение или что-то еще, что позволяет мне проверять (во время компиляции или времени выполнения), если метод действителен (т.е. удовлетворяет моим ограничениям)?
Вернее, есть ли способ принудить его к вызову только ограниченного набора других методов?

Если нет (и я думаю, что нет), будет ли этот второй подход подходящей альтернативой?
Подходит, как в интуитивно понятной, хорошо продуманной и/или эффективной практике.

Обновление (прогресс)

Изучив некоторые связанные вопросы, я также рассматриваю (как третий вариант, возможно) следующие шаги, приведенные в принятом ответе этого вопроса, Хотя это может потребовать некоторого пересмотра архитектуры.

Вся идея использования аннотаций для введения ограничений, похоже, требует внедрения моего собственного обработчика аннотаций. Если это так, я мог бы также рассмотреть небольшой язык, специфичный для домена, чтобы программист использовал эти ограниченные операции, а затем перевел код на Java. Таким образом, я бы также имел контроль над тем, что указано.

Ответ 1

Я думаю, что направление в этом вопросе хорошее.

  • Используйте специальный 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 -like

В Android компоненты системы довольно закрыты. Они не могут напрямую общаться друг с другом, они могут только инициировать событие, что "это случилось", или "Я хочу это сделать".

Таким образом, набор используемых команд не ограничен. Обычно, если методы выполняют только малую бизнес-логику, этого достаточно.

Ответ 2

Посмотрите файлы java-политики. Я не использовал их, и я не уверен, что они точно подойдут вашей проблеме, но с некоторыми копаниями в документах они могут быть пригодными. Вот пара вопросов, которые могут помочь

Ограничение доступа к файлам в Java

Что такое простая политика безопасности Java для ограничения записи файлов в один каталог?

И вот какая-то документация по файлу политики.

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

Ответ 3

Вы можете ограничить классы, используемые ненадежным кодом, с помощью специального загрузчика классов:

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

Ответ 4

Другой альтернативой будет использование интерпретатора en embedded script, например groovy one (http://groovy.codehaus.org/Embedding+Groovy) и для оценки контент сторонних методов во время выполнения с проверкой предварительного исполнения.

Преимущество в том, что вы сможете ограничить доступ только к переменным, которые вы привяжете для выполнения script.

Вы также можете написать свою собственную проверку dsl и применить ее, например, используя пользовательскую аннотацию, к методу, который будет выполнять script.

Ответ 5

Существует несколько дизайн по контракту для Java, но я не могу рекомендовать их в частности. Java Argument Validation представляется легким решением, но опять же, у меня нет непосредственного опыта с ним.