Наложение ограничений или ограничений на тело метода, в 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 представляется легким решением, но опять же, у меня нет непосредственного опыта с ним.