JYaml в качестве DSL

Есть вот такая вот интересная задачка: приложение должно выполнять серию команд, внешне даже однотипных и настраиваемых через разные параметры. При этом описание очереди команд должно быть не в коде, а во внешнем файле, который можно отредактировать, и, без перекомпиляции приложения, запустить очередь заново. Да и вообще файл со списком команд указывать каким то параметром приложению, т.к. будут различные файлы с разными очередностями команд.

Этакий файл с описание фильтров для обрабатываемой информации, который редактируют не обязательно программисты.
Как варианты для реализации: придумать какой то свой формат, потом его интерпретировать. Но такой интерпретатор та еще задача, кучу времени потребует. Еще можно воспользоваться 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'ный, это тоже вполне реализуемо.

  • http:// xeye

    для этого существует груви 🙂

  • http:// igor

    Согласен 🙂 Ну и jruby тоже неплох.