JYaml в качестве DSL
13 апреля 2007
Есть вот такая вот интересная задачка: приложение должно выполнять серию команд, внешне даже однотипных и настраиваемых через разные параметры. При этом описание очереди команд должно быть не в коде, а во внешнем файле, который можно отредактировать, и, без перекомпиляции приложения, запустить очередь заново. Да и вообще файл со списком команд указывать каким то параметром приложению, т.к. будут различные файлы с разными очередностями команд.
Этакий файл с описание фильтров для обрабатываемой информации, который редактируют не обязательно программисты.
Как варианты для реализации: придумать какой то свой формат, потом его интерпретировать. Но такой интерпретатор та еще задача, кучу времени потребует. Еще можно воспользоваться XML, из которого через Castor, к примеру, воссоздавать все наши объекты / фильтры / команды. Но вот какого объёма будет требуемый нам файл с описанием, страшно представить.
Ну а так как это получается некий узкоспециализированный язык, то я вот и предложу как упростить эту задачу. Идея растет из того же XML, но в более упрощённом виде, в виде YAML описания.
Итак, нам, к примеру, надо следующие команды:
- замена в определённом поле
- удаление по определённому регекспу
- сортировка
- удаление пробелов
Получаем вот такой вот файлик:
--- !replace
from: __UNKNOWN__
to: Computer
atField: author
--- !remove
regex: [^\\d]+
atFields:
- year
- phone
--- !sort
withComparator: !defaultComparator
direction: desc
--- !transform
withTransformer: !trim{}
Этакий Domain Specific Language получается. Может на первый взгляд не так все просто, но если написать небольшое описание синтаксиса нашего языка, или знакомство с документацией по yaml, все встанет на свои места, и редактирование подобного списка команд становится несложной задачей, с которой может справится продвинутый пользователь. В общем я сейчас сильно в синтаксис не буду вдаваться, кому надо глянут на yaml.org и jyaml.sourceforge.net
Второй момент как это прочитать, т.е. как построить их этого файла наши объекты. Делается все просто, через упомянутый JYaml:
List<Command> list = new ArrayList<Command>();
YamlDecoder dec = new YamlDecoder(in);
try {
while (true) {
Command command = (Command) dec.readObject();
list.add(command);
}
} catch (EOFException e) {
log.debug("Finished reading stream.");
} finally {
dec.close();
}
ну и т.к. в файле мы использовали не полный имена классов, то надо настроить связь между именем и самим классом, это нужно сделать до чтения самих объектов, следующим образом:
BiDirectionalMap<String, String> classes = new BiDirectionalMap<String, String>();
classes.put("replace", ReplaceCmd.class.getName());
classes.put("remove", RemoveCmd.class.getName());
classes.put("trim", Trim.class.getName());
classes.put("sort", SortCmd.class.getName());
classes.put("defaultComparator", DefaultComparatorCmd.class.getName());
classes.put("transform", TransformCmd.class.getName());
YamlConfig.getDefaultConfig().setTransfers(classes);
На самом деле можно такой мини язык делать не только для списка команд, в общем то почти любой класс так можно описать и десериализовать после в объект, а текущая задача она применима к моим условиям. Правда тут возникла проблема: хотелось бы чтобы эти объекты помимо описания в этом файле имели настройки в Spring. Т.е. все связи с другими объектами настроить в Spring контексте, а саму очерёдность и дополнительные настройки брать уже из этого файла. Так вот это я не знаю как реализовать, не расширяя YamlDecoder. Хотя, т.к. jyaml проект open source’ный, это тоже вполне реализуемо.

июля 2, 2007 в 07:36
для этого существует груви
июля 2, 2007 в 07:39
Согласен
Ну и jruby тоже неплох.