20 апреля 2010 г.

JFormattedTextField.selectAll

Нужно было сделать, чтобы JFormattedTextField выделял свое содержимое при получении фокуса. Стандартный addFocusListener -> .selectAll не сработал. Оказывается (см Sun bug #4740914) JFormattedTextField переопределяет processFocusEvent и внутри сначала зовет базовую реализацию (которая, в числе прочего, и FocusListener-ы оповещает), а потом переформатирует свое содержимое -- что, разумеется, все выделение напрочь убивает.

Решений нашлось два. Либо уныло-универсальное addFocusListener -> SwingUtilities.invokeLater -> selectAll, либо отнаследовать свой собственный класс от JFTF, и переопределить processFocusEvent, дописав в конец selectAll().

Второй метод мне нравится несколько более, но использовать пришлось первый, потому что создание JFTF в моем случае находится вне моего кода, и заменить JFTF на свою реализацию я не смогу без нерационально большого геммороя.

Вообще, в свинге очень много мест, где приходится использовать invokeLater без явной надобности -- просто потому, что черт ногу сломит разбираться с точной последовательностью обработки каких-либо событий.

4 апреля 2010 г.

Вышел jdk 1.6 под Snow Leopard

В последнем обновлении наконец-то появилась шестая джава под мак (SL).

Тут же появились и проблемы с ней. java.io.IOException: Keystore was tampered with, or password was incorrect -- вылетает при работе с SSL ссылка

Корень проблем оказался прост -- эппл сменила (скорее всего, просто по ошибке) дефолтный пароль на хранилище сертификатов с changeit на changeme. Если сменить его обратно -- все заработает как встарь. Например, вот так:

sudo -i
cd /System/Library/Frameworks/JavaVM.framework/Home/lib/security/
keytool -storepasswd -keystore cacerts

1 апреля 2010 г.

Кодировка консоли в java

Кодировка консоли в Win не совпадает с системной кодировкой (которая Charset.defaultCharset()). А знать ее часто бывает полезно. Например, запускаемые внешние процессы могут возвращать описания ошибок на русском -- и фиг вы их прочитаете. Конечно, можно просто зашить кодировку Cp866 для русской версии -- но это неправославное решение. А ну как другая локаль будет? Хотелось бы иметь возможность спросить, в какой кодировке работает наша консоль.

Я долгое время был уверен, что такого метода в jdk не предусмотрено. Но, совершенно случайно, раскапывая исходники стандартной библиотеки обнаружил решение для Sun JDK 1.6+ -- приватный статический метод java.io.Console.encoding()

final Class<Console> clazz = Console.class;
final Method method = clazz.getDeclaredMethod( "encoding", new Class[0] );
method.setAccessible( true );
final String encoding = ( String )method.invoke( null );


возвращает на моей системе Cp866, как положено.

23 марта 2010 г.

Scala

Первое впечатление от Scala IDEA plugin: "как же медленно оно компилируется!" Натуральный С++ камбэк. Успеваю заметить штук 5 стадий компиляции. Все-таки javac, пересобирающий проект из 500 файлов за 20 секунд сильно развращает.

18 марта 2010 г.

Зачем synchronized вокруг wait/notify?

Еще один из вопросов, который мучил меня во время знакомства с java threading, и так и остался, в то время, не отвеченным. Зачем сановские инженеры спроектировали wait/notify так, что они обязательно требуют на входе захваченной блокировки? Причем на входе они ее требуют, но внутри себя они ее отпускают -- что-то очень хитрое стояло за таким решением, что-то, что я не мог понять.

Ответ оказался довольно прост. Другое поведение не имеет смысла, потому что не дает возможность реализовать то, для чего нужен wait/notify.

Зачем нужен wait? Точнее -- каков сценарий его использования? Если мы хотим просто приостановить поток -- есть Thread.sleep(). А wait нужен тогда, когда мы ждем какого-то события. Еще точнее, мы ждем выполнения какого-то условия, за которое отвечает какой-то другой поток. Но это означает, что у нашего потока с этим другим потоком есть общее, разделяемое состояние (shared state). Второй поток это состояние меняет, и в какой-то момент оно становится "подходящим" для нас, и мы хотим об этом узнать. И wait/notify это всего лишь инструмент, который дает нам такую возможность. Но если у нас есть разделяемое состояние -- нам просто необходима блокировка в обоих потоках, чтобы избежать проблем с data race и memory visibility. Сами-то по себе методы wait/notify можно организовать без требования блокировки -- но они будут бесполезны

На конкретном примере: вот как выглядит типичный код с wait/notify (представим, что секции synchronized не обязательны)
//общие переменные 
    boolean condition = false;
    final Object event = new Object();
    ...
    //первый поток
    while(!condition){
        event.wait();
    }
    ...
    //второй поток
    condition = true;
    event.notify();

Что помешает второму потоку вклиниться между строчками 7 и 8 -- когда первый уже решил, что он должен ждать, но еще не вызвал метод wait? В этом случае notify() вызванный вторым уйдет в пустоту (пока еще никто ничего не ждет), а wait(), вызванный первым никогда не пробудится -- некому больше будить (забудем пока про внезапные пробуждения). Другой вопрос -- кто гарантирует, что обновленное вторым потоком значение condition=true будет увидено первым? Никаких memory barrier-ов здесь нет, первый поток спокойно может закэшировать condition хоть в регистрах процессора, и быть свято уверенным, что оно все еще false. Еще можно вообразить разнообразные insruction reordering, в ходе которых компилятор может решить переставить condition = true после event.notify(), например.

Ок, договорились: синхронизация необходима. Но зачем так плотно привязывать wait/notify к synchronized? Спроектировали бы wait/notify независимо от синхронизации, и просто указывали бы в recommended practices что правильно писать так-то. Были бы очевидные бенефиты -- сейчас, например, старый-добрый Object.wait/notify работает только со старым-добрым synchronized, а новый Lock.lock()/unlock() только с новым же Condition.await/signal -- а между собой они не работают, что, в общем-то, странно. Казалось бы -- какая разница, каким методом обеспечивать синхронизацию разделяемого состояния между потоками?

Ан нет, без плотного связывания не получится. wait обязан знать многое о мониторе синхронизации, с помощью которого согласовывается разделяемое состояние -- потому что wait должен уметь этот монитор сначала отпустить на входе, а потом захватить заново -- на выходе. Как минимум, wait должен иметь доступ к этому монитору.

Вот мы и приходим к той реализации, что имеем. Сначала нужно захватить монитор какого-нибудь объекта, чтобы обеспечить согласованность состояния condition. Потом можно вызвать wait(), и он должен захваченный монитор отпустить -- иначе как другой поток сможет изменять условие? Но wait нужно вызвать на том же объекте -- иначе как wait() узнает, какой именно монитор из 10 захваченных текущим потоком выше по стеку ему надо освободить? Перед notify нужно захватить тот же самый монитор, чтобы синхронизировать изменения в condition между потоками. Ну и очевидно, что notify() нужно вызывать на том же объекте, чтобы он знал, какой wait ему нужно будить. Такой вот расклад

Чем плохо synchronized(this)?

Несколько лет назад, бродя по исходникам JDK я задался вопросом -- почему там так часто встречается организация блокировки через
private final Object lock = new Object();
....

synchronized(lock){
...
}

хотя для этих целей легко можно использовать synchronized(this)? Я еще понимаю, если нужно несколько мониторов для разных наборов атомарных изменений (хотя, на мой взгляд, это часто первый звоночек что объекту нужна декомпозиция), но я часто видел такой код и когда монитор только один. Какое-то время это было для меня загадкой, пока у кого-то из гуру я не встретил ответа -- использование this как монитора синхронизации может нарушать инкапсуляцию вашего объекта. Монитор синхронизации -- это штука с состоянием: у него есть владелец (owner) который может меняться. Давая возможность клиентам работать с вашим объектом вы не можете запретить им работать с его монитором -- а это может менять его состояние, и нарушать те инварианты, на которые вы рассчитывали, проектируя класс.

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

Но вот вчера у меня, наконец, сложился явный и четкий пример. Итак, код:
public class Runner {

private Thread owner = null;

public synchronized void run( final Callable task ) throws Exception {
    owner = Thread.currentThread();
    try {
        task.call();
    } finally {
        owner = null;
    }
}

public synchronized void check() {
    final boolean invariant = ( owner == null ) || ( owner == Thread.currentThread() );
    if ( !invariant ) {
       //can this code be executed?
       throw new AssertionError( "Lock is broken: " + owner + " is owner, but " + Thread.currentThread() + " is here!" );
    }
}

}

на первый взгляд кажется, что код в строках 17-18 никогда не может быть выполнен. Оба метода синхронизированы, никакой посторонний поток не может влезть, пока текущий поток внутри run(Callable). Однако, сломать этот объект крайне просто:
final Runner runner = new Runner();

final Callable task = new Callable() {
    public Object call() throws Exception {
        runner.wait( 2000 );
        return null;
    }
};

final Thread thread = new Thread( "runner" ) {
    public void run() {
        try {
            runner.run( task );
        } catch ( Exception e ) {
            throw new RuntimeException( e );
        }
    }
};

thread.start();

//ensure thread started
Thread.yield();
Thread.sleep( 100 );
//check the invariant
runner.check();

object.wait() должен вызываться внутри synchronized(object), и, на время ожидания он отпускает монитор. То есть пока поток thread ждет 2 секунды на мониторе runner этот монитор свободен, несмотря на то, что выше по стеку есть synchronized(runner). И в эти 2 секунды любой другой поток может захватить этот монитор -- что мы и делаем, демонстрируя нарушение инварианта класса.

Какой отсюда вывод? Вывод такой: если вы используете callback интерфейсы -- т.е. если ваш код выполняет внутри себя какой-то другой код, пришедший "со стороны" -- вы должны делать это либо вне синхронизации, либо использовать монитор синхронизации, до которого клиентский код не сможет добраться, вроде private final Object lock = new Object()

На данный момент я не вижу других способов (кроме callback), как "открытая" синхронизация с помощью synchronized(this) может нарушить инкапсуляцию. Если класс колбэки не использует -- можно использовать синхронизированные методы спокойно.

17 марта 2010 г.

Чем отличается synchronized метод от synchronized(this) блока?

Аттрибут synchronized -- это флаг на методе. Компилятор не будет вставлять на выходе и выходе из метода инструкции захвата и освобождения монитора, как в случае с synchronized(this) блоком; вместо этого уже JVM, на стадии выполнения кода фиксирует наличие этого флага, и автоматически выполняет требуемый захват и освобождение монитора. То есть все различие -- флаг в описании метода вместо двух байт-кодов в его теле. Возникает вопрос: стоит ли беспокоиться из-за двух инструкций (инструкций байт-кода, не процессора!) Чаще всего -- нет. Но представьте себе, например, что JIT-компилятор использует количество байт-кодов в теле метода как одну из метрик для принятия решения о его инлайнировании? Что, кстати, почти наверняка так и есть...

Вольный перевод отсюда (сноска в конце страницы)

Ну так и в java коде синхронизированный метод выглядит заметно компактнее, чем метод, все тело которого обернуто в синхронизированный блок. Может, разработчики языка тем самым хотели на что-то намекнуть?

Синхронизация

Читаю серию статей от Neil Coffey по синхронизации и вообще многопоточному программированию в java. Неожиданно много вещей, которых я не помню или не знаю. Все-таки вещи, которые мало используешь плохо запоминаются -- я смутно помню, что почти все где-то когда-то встречал, но в общую картину у меня в голове все тонкости так и не собрались -- не было достаточно крупных и сложных задач по многопоточности в моей практике.

В ближайшее время постараюсь уложить все у себя в голове в единую картинку, и написать сюда пару статей. Особенно хочется разобраться с volatile и Thread.interrupt(). Заодно, может быть, пока буду готовить статьи -- и сам, наконец, запомню :)

12 марта 2010 г.

Serialization libraries

Наткнулся на интересный "проект" по сравнению производительности различных способов/библиотек сериализации/десериализации в джава. Comparing varius aspects of Serialization libraries on the JVM platform Мало того, что лишний раз подивился на разницу в скорости стандартной сериализации с Externalizable, так еще и узнал о куче интересных библиотек. Например, Kryo выглядит вполне подходящей заменой стандартному механизму -- быстрее, нагляднее, архитектурно изящнее (механизмы сериализации настраиваются отдельно от сериализуемых объектов -- в отличие от стандартного метода, где объект в любом случае сам задает свой метод сериализации, и он может быть только один). А JSON Marshaller я собираюсь рассмотреть на место XStream в DataGuard -- все равно XStream-ский json даже после тщательной доработки напильником периодически генерирует ересь. Более того, в json marshaller есть и замена для org.json.* -- по их словам она более быстрая и более удобная.

11 марта 2010 г.

Со-процедуры (coroutines)

Не знаю русского эквивалента термина coroutines. Собственно, сам английский термин я узнал только сегодня -- хотя отдельные варианты сопроцедур -- генераторы и продолжения (continuations) -- встречал и раньше.

Так вот, сопроцедуры. Это такая штука, когда, наряду с обычным return вводится дополнительный способ завершения функции/процедуры -- обычно, его называют yield. В результате yield выполнение функции не завершается, а приостанавливается. Сохраняется состояние стека, локальные переменные. И в следующий раз, при вызове этой функции выполнение просто продолжится с того же самого места, где в прошлый раз был вызван yield.

Зачем это нужно? Ну во многих случаях это сильно упрощает программу. Например, я знаю веб-фреймворк для джавы , построенный на продолжениях (сontinuations, частный случай), где весь цикл взаимодействия с браузером может быть реализован в прямом смысле циклом -- for/while -- внутри одного метода (точнее, фреймворк написан на джаве, но код веб-приложения под него пишется на javascript/rhino). Когда нужно отправить данные пользователю ваш код просто вызывает что-то вроде var userResponse = postToUser(htmlPage); -- выполнение вашего кода приостанавливается на вызове postToUser, страница отправляется пользователю, он с ней что-то делает, результат отправляется на сервер -- и ваш код его получает в переменную userResponse, продолжая выполнение дальше. Очень изящно, гораздо проще, чем сервлеты.

Другой вариант -- всем известные генераторы, как замена итераторам.

В общем, штука удобная и интересная. К тому же, если верить автору, еще и достаточно быстрая. Если она в самом деле будет включена в jdk1.7 -- будет приятно.


Источник: http://classparser.blogspot.com/ via Levin Matveev blog

Syntax highlighter

Нашел себе подсветку синтаксиса для скриптов в блоге.

В теле поста пишем
<pre class="brush: java">
public static void main( final String[] args){
final int count = args.length;
System.out.println("count: "+count);
}
</pre>




и в результате получаем:
public static void main( final String[] args){
    final int count = args.length;
    System.out.println("count: "+count);
}


преобразование делает JavaScript, подгружаемый в начале страницы. В предыдущих постах я использовал highlighter, выдающий готовый html-код, который надо было копипастить в пост. В итоге получалось, что код из двух строчек с подсветкой занимает пол страницы, причем собственно код в этой мешанине html-тегов уже совершенно не видно -- неудобно.

Подробности здесь: Awesome syntax highlighting

Numbers everyone should know

Numbers everyone (developer) should know (от разработчиков google)

* L1 cache reference 0.5 ns
* Branch mispredict 5 ns
* L2 cache reference 7 ns
* Mutex lock/unlock 100 ns
* Main memory reference 100 ns
* Compress 1K bytes with Zippy 10,000 ns
* Send 2K bytes over 1 Gbps network 20,000 ns
* Read 1 MB sequentially from memory 250,000 ns
* Round trip within same datacenter 500,000 ns
* Disk seek 10,000,000 ns
* Read 1 MB sequentially from network 10,000,000 ns
* Read 1 MB sequentially from disk 30,000,000 ns
* Send packet CA->Netherlands->CA 150,000,000 ns

9 марта 2010 г.

Smack XMPP

На выходных игрался с XMPP. Недавно на хабре был анонс простенькой текстовой игры через jabber: snow@talk2play.ru Мне поднадоело играть в нее самому, захотелось это дело автоматизировать. В итоге, после 3-х дней отладки мой бот более-менее устойчиво набирает очки. Нет ничего приятнее, чем смотреть, как кто-то делает твою работу...

Джабберовский XMPP протокол (и его реализация в Smack) произвел хорошее впечатление. Простой и расширяемый. В будущих проектах собираюсь попробовать предоставлять network interface через него -- на пару к обычному HTTP.

5 марта 2010 г.

Запуск внешних программ

Недавно в DataGuard пришлось разбираться с запуском внешних программ из java. Некоторые вещи оказались довольно нетривиальны, так что я решил поделиться.

На первый взгляд, все достаточно просто -- для простых случаев есть Runtime.exec(), если нужно настроить параметры среды для запуска -- есть ProcessBuilder. В любом случае получаем объект Process, у которого вызываем Process.waitFor(), чтобы дождаться завершения -- и, вроде бы, все?

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

Главный из них -- потоки ввода-вывода (IO streams). У порождаемого процесса нет терминала, к которому он привязан, его stdin, stdout, stderr выдаются порождающему процессу -- то есть, нам. Причем обрабатывать их -- наша обязанность. Потоки, созданные ОС имеют ограниченный размер буфера. Если, к примеру, буфер stdout для запущенного процесса заполнен, со стороны java никто его не читает (==не освобождает) а процесс настойчиво хочет что-то вывести -- то процесс просто окажется заблокирован на IO, и будет ждать, пока stdout кто-нибудь освободит. Если мы не предусмотрели в java код, читающий process.getInputStream() -- получается стандартный дедлок: мы ждем завершения процесса, процесс ждет нас.

Самый опасный момент здесь в том, что размер буфера заранее не определен. Поэтому приложение может в одном случае работать как часы, а в другом -- непонятно зависать.

Конкретный пример: в обычном, штатном режиме работы внешний процесс выдает одну-единственную строчку "Ок" и завершается. Строчка вполне влезает в буфер, поэтому код

final Process p = Runtime.getRuntime().exec( "my-script.bat" );  
final int retCode = p.waitFor();  

работает корректно. Но наступает день Х, когда звезды складываются неудачно. И процесс завершается с ошибкой. И, как и положено уважающей себя программе, старается эту ошибку максимально подробно описать. И пытается вывести в stdout простыню текста, превышающую размер буфера. Вуаля -- процесс ждет на выводе, java-программа -- на process.waitFor()

На мой взгляд -- это пример плохо спроектированного API. Простая вещь -- запустить внешний процесс не заморачиваясь с его выводом -- делается весьма нетривиально. Более того, из самого API это никак не следует. Да, в документации к Process это прописано, но я считаю, что хороший API это такой, использование которого, по крайней мере для простых задач, очевидно без документации. Можно было бы дополнить контракт, например, так: "если клиент не запросил process.getInputStream()/process.getErrorStream() до вызова process.waitFor() -- stdout/stderr внешнего процесса автоматически перенаправляются вникуда".

Но наши друзья из Sun этого не сделали, так что приходится отдуваться самим: ProcessRunner

Что делает: берет сконфигурированный ProcessBuilder, создает внешний процесс, запускает асинхронно "помпы", прокачивающие его потоки ввода-вывода либо в пустоту (если пользователь ничего не задал) либо из/в заранее заданные потоки. Метод ProcessRunner.execute() блокируется пока либо процесс не завершится, либо пока не будет вызван ProcessRunner.interrupt(). Пример использования:
final ProcessBuilder pb = new ProcessBuilder("my-script.bat");  
final ExecutorService pool = Executors.newFixedThreadPool(3); // нужно минимум 3 свободных потока в пуле 
final ProcessRunner pwd = new ProcessRunner( "run", pb,  pool );  

pwd.execute();  

final int retCode = pwd.getReturnCode();  
...
pool.shutdown();


В этом примере ввод-вывод my-script.bat будет просто выброшен. Другой пример:
final ProcessBuilder pb = ...;  
final ProcessRunner pwd = new ProcessRunner( "run", pb, POOL );  

final ByteArrayOutputStream out = new ByteArrayOutputStream();  
final ByteArrayOutputStream err = new ByteArrayOutputStream();  
pwd.setOutputStream( out );  
pwd.setErrorStream( err );  

pwd.execute();  

assertEquals( 0, pwd.getReturnCode() );  
final byte[] output = out.toByteArray();  
final byte[] errors = err.toByteArray();  

Здесь stdout/stderr будут считаны в предоставленные нами потоки. Обратите внимание, что если флаг ProcessBuilder.redirectErrorStream() выставлен в true, то stderr будет слит с stdout, и errors будет пуст.

Больше примеров использования можно посмотреть в тестах ProcessRunnerTest

25 февраля 2010 г.

Data Oriented Programming вместо ООП

Пара интересных статей:

Musings on Data Oriented Design

Pitfalls of Object Oriented Programming (GCAP)

Суть вопроса, который поднимают авторы, такова -- за последние 30 лет развития IT скорость выполнения команд процессором выросла почти в 50000 раз, но время ответа памяти на запрос (memory latency) уменьшилось всего лишь раз в 10. В итоге мы получаем, что "стоимость" запроса к памяти (в циклах процессора) выросла примерно в 400 раз.

При этом современные компиляторы очень хорошо умеют работать с кодом -- фактически, машинный код, получаемый на выходе из компилятора может вообще не иметь ничего общего, с тем, что изначально было написано человеком -- кроме того, что делает то же самое. Но компиляторы очень мало что делают с данными -- размещение данных в памяти (data layout) генерируемое компилятором фактически очень мало отличается от того, что задекларировал программист в коде (разве что компилятор добавит туда от себя что-то -- vtable например, да выровняет по границам машинных слов). Хотя при современном состоянии дел с memory latency было бы гораздо выгоднее оптимизировать расположение данных под выполняемые задачи.

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

Вместо этого предлагается программисту (пока компиляторы не научились) продумывать размещение данных в памяти исходя из того, как они будут обрабатываться. В частности, в презентации (pdf) Том Албрехт показывает, как им удалось в 3-4 раза ускорить обновление-прорисовку дерева объектов сцены (scene tree) за счет изменения компоновки данных (там, правда, еще про предсказание ветвлений упомянуто)

15 февраля 2010 г.

JCaptcha

Разбирался на прошлой неделе с JCaptcha. Хорошая библиотека, но документация -- отвратная. Огромный разрыв между beginners guide и реальным использованием никак не покрыт. То есть начинаешь читать 5 minutes integration -- все вроде просто. Шаг чуть дальше -- нигде кроме как в исходниках инфы не найдешь.

Последний раз так с MFC мучался в дремучих 90-х. Думал уже, в мире джава таких проектов нет...

Вот, для примера, "переведенный" мною с языка spring-configuration на java код инициализации службы генерации капч:

private static final ImageCaptchaService instance;// = new DefaultManageableImageCaptchaService();

private static final int FONT_SIZE_MIN = 25;
private static final int FONT_SIZE_MAX = 29;

private static final int HEIGHT = 40;
private static final int WIDTH = 200;

static {
//Шрифт, которым будет написана капча. Размер не важен
final Font font = new Font( "Times New Roman", Font.PLAIN, 12 );

//разные буквы разными шрифтами разного размера
final FontGenerator fontGen = new RandomFontGenerator( FONT_SIZE_MIN, FONT_SIZE_MAX, new Font[]{ font } );
//...и разными цветами 
final ColorGenerator colorGen = new RandomListColorGenerator( new Color[]{ Color.RED, Color.GREEN, Color.BLUE, Color.BLACK } );
//однотонный белый фон-подложка. здесь, по-сути, задается размер капчи
final BackgroundGenerator bgGen = new UniColorBackgroundGenerator( WIDTH, HEIGHT, Color.WHITE );
//вырезаем слова от 6 до 9 букв
final TextPaster textPaster = new RandomTextPaster( 6, 9, colorGen );
//список слов можно задать 
final FileDictionary dict = new FileDictionary( "toddlist" );
final WordGenerator wordGen = new DictionaryWordGenerator( dict );
//final WordGenerator wordGen = new ComposeDictionaryWordGenerator( dict );

final ComposedWordToImage wordToImage = new ComposedWordToImage( fontGen, bgGen, textPaster );

final CaptchaFactory factory = new GimpyFactory( wordGen, wordToImage );

final GenericCaptchaEngine engine = new GenericCaptchaEngine( new CaptchaFactory[]{ factory } );

instance = new GenericManageableCaptchaService( engine, 300, 500000, 1 );
}

12 февраля 2010 г.

JavaMail + SSL

Продолжение разбирательств. Краткий итог: итак, самый простой и надежный метод работы с SSL -- использовать
System.setProperty("mail.smtp.ssl.enable","true");

Все остальные настройки как в обычном smtp (только порт обычно 465 вместо 25). Работает с gmail -- проверено

Подробное описание:

Вариант с
System.setProperty("mail.transport.protocol", "smtps")

не работает. Точнее, не совсем работает -- не так, как ожидается. Раскопки в исходниках JavaMail (благо, они доступны) показывают, что свойство mail.transport.protocol используется только методом Session.getTransport() (который без аргументов). Но самой библиотекой этот метод не используется -- предполагается, что его вызывает клиентский код явно. Если вы отправляете почту через явно создаваемый транспорт, и получаете его через session.getTransport() -- свойство mail.transport.protocol будем иметь эффект. Если же вы, как я, отправляете почту через статический метод Transport.send(), то внутри него библиотека ищет подходящий транспорт через вызовом getTransport(Address), что отсылает нас к свойству mail.transport.protocol.rfc822 (rfc822 -- такой "тип адреса" у адреса электронной почты). Если хочется переопределить транспорт для такого типа адресов на smpts -- надо писать
System.setProperty("mail.transport.protocol.rfc822", "smtps");
, либо использовать session.setProtocolForAddress("rfc822", "smpts"). Если просто написать mail.transport.protocol = smtps, то Transport.send() это указание проигнорирует.

В числе прочего это означает, что если вы вместе с mail.transport.protocol = smtps укажете, как описано в документации, свойства типа mail.smpts.host -- они будут проигнорированы (использоваться будут свойства типа mail.smtp.host) и JavaMail будет ломиться на localhost (который используется в качестве адреса smtp сервера по-умолчанию).

JavaMail + SSL

Намучался искать нормальный (авторитетный) источник информации по отправке secure mail с помощью JavaMail. Secure -- это который через SSL и порт 465 (обычно).

В итоге нашел описание на самом java.sun.com (ссылка в заголовке).

Если я их правильно понял, есть 3 основных варианта (для самой свежей версии JavaMail -- 1.4.3)

Первый вариант просто указать системное свойство mail.smtp.ssl.enable=true. Провайдер вроде как должен сам понять, если имеет дело с секьюрным сервером.

Второй вариант -- последней версии можно использовать имя протокола smtps (pops, imaps) -- и, соответственно, все свойства задавать в виде mail.smtps.host, mail.smtps.port, etc.


Последний, самый старый вариант -- инсталлировать SSLSocketFactory через системное свойство mail.smtp.ssl.socketFactory.class

И еще гмайл и похожие сервера требуют установки свойства mail.smtp.starttls.enable=true.

В общем, JavaMail как обычно напоминает шаманские пляски.

5 февраля 2010 г.

String.trim()

Удивительно, но String.trim() не отбрасывает символ неразрываемого пробела 0x00A0. Я был просто поражен -- и нет даже никакой опции, как это убрать.

Более того, \s в регулярках тоже не считает 0x00A0 пробельным символом! Пришлось извращаться в духе [\s\u00A0]

3 февраля 2010 г.

Gallery of Processor Cache Effects

Блог разработчика из Микрософт Игоря Островского. Влияние кэша процессора на производительность