Archive for the ‘Код и программистское’ Category

По пути наибольшего сопротивления.

Пятница, Апрель 1st, 2011

У меня такое впечатление, что у меня где-то в генокоде прописано то, что я постоянно в программировании двигаюсь по пути наибольшего сопротивления.

То есть, если мне нужно сделать, что-то с новой (для меня) технологией, то я последовательно наступаю на ВСЕ грабли, который можно найти.

Сегодняшний пример.
Мне нужно написать небольшой Servlet (условно говоря 1 страница кода) и отмерять jMeter’ом его быстродействие. Ну блин, что может быть проще?

Ан… нет. Учитывая, что сервлеты я писал дааавно (да и JSP’шки, разве что самые простенькие делал), то естественно я успел наткнуться на странные грабли.

Посылаю POST (из jMeter), а в Servlet приходит GET. Ладно, сделал HTML, посылаю форму POST, все равно зараза приходит GET. В общим, в инете рылся, ничего не нашел по этому поводу. Плюнул, Нашел пример сервлета, запустил – для него таки приходит POST.

Ok. Ищем разницу. Нашел таки. Оказывается, в порыве страсти, когда я делал servlet-mapping, то url-pattern я указал «/»
Соотвественно, весь URL у меня должен выглядеть так «http://localhost:8080//». А я вместо этого и в jMeter и в HTML написал «http://localhost:8080/» (без последнего слеша).

И вот эта зараза, вместо того, чтобы послать меня нафиг или работать нормально, решила, что вместо POST, она будет делать GET, а все параметры выкинем в окно.

Бац-бац-бац головой об стену.

Доктор, дайте мне пилюль от граблей.

Вопрос по JNI и JEE к джавистам.

Среда, Март 23rd, 2011

Условия:
а) Есть DLL в которой нужная нам функциональность
б) Есть самописанная JNI обертку (проверил на JSE – работает на ура)
в) Есть JBoss на виндах

Вопрос:
в) Как бы по правильному использовать JNI в JEE envinronment (под JBoss).

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

Из того, что я прочел и понял
- Если у нас что-то крешится в DLL, то падает весь сервер.
- Если Java класс делающий System.LoadLibrary загружен одним ClassLoader’ом, то если второй ClassLoader попытается
загрузить тот же класс, то оно не сможет загрузить DLL, так как DLL уже загружен в процессе (особенность винды) и поэтому не сможет использовать JNI интерфейсы.
- Как я понял, по спецификации EJB не работают с JNI. С другой стороны, в нескольких местах прочел, что работаю, но с ограничениями.

Пару идей которые встречал
а) Сделать C++ приложение JMS enabled. И общаться из приложения в app server с этим JMS enabled приложение
б) Поднять отдельный сервер и общаться через RMI.
в) Сделать adaptor (JavaEE connector architecture). Это опцию я понял хуже всего, но зато похоже она наиболее правильная.

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

По следам опыта создания DLL’ек.

Пятница, Март 18th, 2011

Ну, что может быть сложного в написании DLL? Ну пишем код, помечаем функции которые экспортируются, компилируем… вуаля… готово.

Ну, это естественно на бумаге, а в жизни, как оказалось множество оврагов. Очень быстро и поверхностно (все пишется с точки зрения C/C++, но на самом деле актуально фактически для всего из чего можно собрать DLL)

- Ну начнем с простого факта. В DLLMain нельзя делать много из того, что можно делать в многих других местах.
а) Вызывать LoadLibrary, FreeLibrary
б) Работать с Registry
в) Работать с потоками и процессами. (Создавать потоки можно, но ждать их нельзя)
г) Использовать API, которые предоставляются не Kernel32.dll
д) И само собой нельзя делать все то, что использует вышеперечисленные вещи. Например нельзя использовать COM

- Увы, факт ограничений в DLL написал маленькими буквами где-то на 5 странице документации. Еще хуже то, что зачастую все будет работать нормально, до тех пор пока вам какой-нибудь заказчик не пришлет баг, которые повторяется в 30% случаев, когда луна находится в перегелии и не зная этих ограничений можно долго и счастливо все это дебажить.

- Ладно. С ограничениями разобрались. Если нам что-то надо будет сделать, мы создадим потом, который сделает эти операции или сделаем глобальную переменную объекта, в конструкторе которого сделаем все что нам надо. Правильно? Бззззз… Ответ не правильный.
а) Если мы создаем поток и не ждем его (так как ждать нельзя), мы не можем быть уверенными что DllMain уже закончится, так что мы просто создали race condition и проблемы стали еще более тяжело воспроизводимы
б) Глобальные переменные и статические члены классов инициализируются и деинициализируются внутри DllMain. Хотя это с ходу не видно, но если полезть в CRT, то это станет понятно. Таким образом, описанные ограничения касаются также конструкторов и деструкторов.

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

- Особенно все прикольно становится, когда мы пишем DLL которую будет впихивать в чужие процессы с помощью SetWindowHookEx или просто plugin’ы к какой-то системе. В обоих случаях, интерфейс может быть заранее определен и там нету функций инициализации и деинициализации. Соотвественно, не ясно что делать.

- Ok. Поздняя инициализация нам поможет насчет инициализации. То есть когда-то кто-то вызовет первый из интерфейсов, мы сделаем все сложные действия. А что делать с деинициализацией? В отличии поздней инициализации, ранней деинициализации не существует то? На это увы, общего ответа я не нашел и в каждом случае, нужно искать свое решение.

- Следующая особенность состоит в том, что все потоки созданные в DLL на самом деле принадлежат процессу. То есть если вдруг DLL будет выгружена из памяти (потому что вызывающий процесс сделал FreeLibrary и DLL counter стал равным 0), то внезапно адресное пространство где была DLL будет высвобождено. А поток останется __ЖИВ__. То есть он попытается выполнить свою следующую операцию, для этого попытается считать следующую команду из адресного пространства где была DLL и мгновенно закрешится. Скажите прекрасно?

- Ok. То есть нам надо остановиться все потоки, которые мы создали в DLL перед тем, как она будет выгружена и это при том, что мы еще даже точно не знаем, как нам запустить какой-то код перед выгрузкой, причем в момент, где нет ограничений. А да, и я еще молчу о том, что написать нормальный thread manager который обрабатывает достаточно большое количество разных ситуаций не вызывая dead lock’ов и race condition, задача в целом не тривиальная.

- Так… Что там у нас дальше. Ага. Вам нужно использовать COM? Само собой вы привыкли к CoInitialize(Ex) и побежали. Только вот, есть одна проблема. Если вы это делаете в DLL то вы исполняетесь в чужом потоке и у потока уже может быть инициализирован COM, причем не в том режиме (STA vs MTA) в котором вам нужно. Что делать? Создавать отдельный поток в котором выполнять все COM действия.

- Единственное пожалуй из положительного. Многое из того, что я писал относится к выгрузке DLL системой. Есть простой трюк, как удостовериться, что система никогда не выгрузит ваш DLL (исключая закрытие программы). Все что вам нужно сделать в DLL это вызывать GetModuleHandle(Ex). Это добавит 1 к DLL counter’у и можно не беспокоиться, что counter когда-либо вернется к нулевому значению.

- А еще во время выхода из процесса, система убивает все потоки, причем поток мог быть посередине модифицирования каких-нибудь данных (внутри критической секции). Еще приятней то, что он мог быть внутри функции работы с heap и таким образом heap будет в inconsistent state. И поэтому лучше не делать ничего хитрого (а по возможность вообще ничего) в DLLMain DLL_PROCESS_DETACH и в деструкторах глобальных объектов (взято отсюда: http://blogs.msdn.com/b/oldnewthing/archive/2007/05/03/2383346.aspx

Вроде все, хотя может что-то еще вспомню.

Поборол опенсорсофобию.

Пятница, Январь 7th, 2011

Раньше, я очень с опаской относился к open source (в смысле, лезть в код, чтобы разобраться как оно работает или не дай бог исправить какой-то баг).

За последние пол года, покопался с хорошей кучей всякого open source:
- Apache
- OpenSSL
- Ruby
- BouncyCastle
- EJBCA
и еще с некоторым количеством менее известных вещей.

Ну, что.. Боязнь лезть в код исчезла. Плюс, даже кинул авторам проектов пару пофиксов, багов, которые по ходу нашел.

Те проекты, которые распространненые, обычно очень неплохо написаны – нормальная архитектура и код, так что разобраться можно.

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