Использование Apache Camel на примере аккаунта Twitter публикующего текущие курсы валют ЦБ РФ
Где-то под новый год сидя в офисе мы со @splix (Игорем Артамоновым) обнаружили дефицит реальных практических примеров использования Apache Camel. В общем догло ли, коротко ли, решили сделать пример по публикации курсов валют с сайта ЦБ РФ в Twitter и чтобы это все работало из коробки одной командой. Сегодня я рад представить вам базовый вариант этой задумки. Посмотреть результаты работы можно здесь.
Реализация сделана полностью на Groovy в виде пяти файлов, два из которых модульный тесты и еще один сборка Gradle. Шаги для запуска примера:
- Установить Sun JDK 1.6+.
- Скачать Gradle 0.9+.
- Установить систему контроля версий Git.
- Забрать пример с GitHub — ‘git clone git@github.com:dulanov/cbrru-agent.git’.
- Скопировать build.properties.template в build.properties и прописать параметры своего аккаунта Twitter.
- Запустить командой ‘gradle -q run’.
Код интеграции говорит сам за себя, спасибо лаконичности Apache Camel DSL:
from('quartz://timer?cron=13+01+15+?+*+MON-FRI') .to('http://www.cbr.ru/scripts/XML_daily.asp') .transform(body(CurrencyRates.class)) .to("log:${this.class.package.name}?level=INFO&multiline=true") .process({twitter.updateStatus(it?.in.body as String)} as Processor)
Остальные детали по адресу http://github.com/dulanov/cbrru-agent.
Переопределение методов equals() и hashCode() в Groovy
Groovy реально позволяет избавиться от поносамногословия Java. Но вчера столкнулся с необходимостью переопределения всем хорошо известных методов equals() и hashCode(). Оказывается, Groovy не упрощает жизнь в этом вопросе. Но у меня не возникло ровно никакого желания писать такую простыню, использовать же попахивающую тухлецом библиотеку Jakarta Commons Lang тоже не было никакого желания.
Что же остается делать? Ну, можно использовать появившийся в версии Groovy 1.6 макрос @Immutable. Но невозможность переопределения конструктора и вообще достаточно сильные накладываемые ограничения на класс не позволили мне этого сделать.
Ок, пошел посмотрел как обстоят дела у наших соседей — языка Ruby, ведь не секрет что очень много в Groovy стырино именно оттуда, а Ruby в свое время скоммуниздил все что надо из Smalltalk. В итоге, как и ожидалось, у Ruby таких проблем в принципе нет и все ограничивается чем-то наподобии:
def ==(other) @suit == other.suit and @kind == other.kind end
Видимо карма такая у Java — все усложнять и там где уместна одна строчка, должно быть десять и непременно парочка XML-файлов конфигурации, чтобы не расслаблялись =)
В общем решил забить и на Java с ее гуру Джошуа Блохом и написать по аналогии как это делается в Ruby:
enum CurrencyCode { USD('$'), EUR('€'), CNY('¥') char symbol CurrencyCode(symbol) { this.symbol = symbol } } class CurrencyRate implements Comparable<CurrencyRate> { def code, value CurrencyRate(aCode, aValue) { (code, value) = [aCode, aValue] } int compareTo(CurrencyRate rate) { this.code <=> rate.code ?: this.value <=> rate.value } int hashCode() { 31 * (code?.hashCode()?: 0) + (value?.hashCode()?: 0) } boolean equals(obj) { obj in CurrencyRate && code == obj.code && value == obj.value } String toString() { String.format "1%c = %.4f р.", code.symbol, value } }
Возможно, этот код нарушит какую-то из непреложных заповедей Джошуа, а может и несколько, зато я получил то, ради чего и начал использовать Groovy — гибкость и лаконичность кода!
Результат двухдневных извращений с SSHTools
Задача: из Java удаленно по SSH запустить приложений и забрать log-файл.
Два дня копался с SSHTools, задолбался, посмотрел JSch — ужаснулся. В итоге лень взяла вверх и тупо решил использовать готовые задачи из Apache Ant sshexec и scp с помощью AntBuilder из GDK (Groovy JDK). Смотрим, что получилось:
// SSHRunner.java import groovy.util.AntBuilder; import java.util.HashMap; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class SSHRunner { private final static Log LOG = LogFactory.getLog(SSHRunner.class); public final static int DEFAULT_SSH_PORT = 22; private int port; private String host, username, password; private AntBuilder builder = new AntBuilder(); public SSHRunner(String host, String username, String password) { this(host, DEFAULT_SSH_PORT, username, password); } public SSHRunner(String host, int port, String username, String password) { if (StringUtils.isEmpty(host) || port < 1 || StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { throw new IllegalArgumentException("All parameters must be at least one character"); } this.host = host; this.port = port; this.username = username; this.password = password; } @SuppressWarnings("serial") public SSHRunner sshexec() { builder.invokeMethod("sshexec", new HashMap() {{ put("host", host); put("trust", "true"); put("port", Integer.toString(port)); put("username", username); put("password", password); put("command", "cscript c:\\test.vbs"); }}); return this; } @SuppressWarnings("serial") public SSHRunner scp(final String filepath) { builder.invokeMethod("scp", new HashMap() {{ put("trust", "true"); put("port", Integer.toString(port)); put("remoteFile", String.format("%s:%s@%s:c:\\testfile.txt", username, password, host)); put("localTofile", filepath); }}); return this; } public static void main(String[] args) { new SSHRunner("127.0.0.1", "admin", "password").sshexec().scp("/home/dulanov/Desktop/test.txt"); } }