Где-то месяц назад я описывал простую систему логирования для web-приложений, которая позволяла, кроме всего прочего, группировать данные в логах в контексте одного запроса. Фича весьма удобная и я с удовольствием использую её в своих проектах. Но, как водится, в процессе эксплуатации вылезли и недоработки. Этот прост о том, как развивалась система, и о том, как она теперь работает.
Что было не так со старой системой?
Да, она группировала вывод в контексте одного запроса к серверу. Вернее в контексте одного потока, запущенного servlet-контейнером для обработки этого запроса. Если servlet не порождал новых потоков, то всё было нормально, но вот если новые потоки создавались, мы тут получали проблему. Логгер писал их отдельно, а значит, вперемешку с остальными потоками. Мелочь, а неприятно.
Решения конкретных задач программирования. Java, Android, JavaScript, Flex и прочее... Настройка софта под Linux, методики разработки и просто размышления.
понедельник, 30 декабря 2013 г.
пятница, 15 ноября 2013 г.
Включаем multitouch и "natural scroll" для touchpad-а Dell Inspiron n5110
Сегодня переехал на "новый" Dell. Поставил свой любимый дистрибутив Elementary OS, с привычным удовольствием настроил софт, разложил всё по местам... И тут - разочарование: прокрутка двумя пальцами, к которой уже привык на предыдущем ноуте не работает. То, что система не предлагала эту опцию навело меня на мысль, что мой touchpad не определился как multitouch-устройство. А ведь это не так! Несколько минут поиска по форумам дали достаточно простое решение:
Скачиваем ALPS dlkm driver
Identifier “touchpad catchall”
Driver “synaptics”
MatchIsTouchpad “on”
Option “VertScrollDelta” “-111”
Option “HorizScrollDelta” “-111”
MatchDevicePath “/dev/input/event*”
EndSection
Скачиваем ALPS dlkm driver
Распаковываем его. Каталог psmouse-alps-1.3 с правами root копируем в /usr/src
Выполняем
Выполняем
sudo dkms add /usr/src/psmouse-alps-1.3
sudo dkms autoinstall
sudo rmmod psmouse
sudo modprobe psmouse
sudo dkms autoinstall
sudo rmmod psmouse
sudo modprobe psmouse
После этого multitouch определяется корректно и мы можем включить нашу любимую опцию.
Если этого не случилось, попробуйте подменить /usr/src/psmouse-alps-1.3/src/alps.c исходниками взятыми отсюда и повторить процедуру.
Если этого не случилось, попробуйте подменить /usr/src/psmouse-alps-1.3/src/alps.c исходниками взятыми отсюда и повторить процедуру.
Собственно вся технология взята тут, судя по коментариям она отлично работает на большинстве linux-дистрибутивов.
Попутно нашёл ещё один полезный фокус для любителей этого няшного дистрибутива. Если вам нравится "natural scroll" в стиле Mac, в файле /usr/share/X11/xorg.conf.d/50-synaptics.conf добавляем строки:
Option "VertScrollDelta" "-111" Попутно нашёл ещё один полезный фокус для любителей этого няшного дистрибутива. Если вам нравится "natural scroll" в стиле Mac, в файле /usr/share/X11/xorg.conf.d/50-synaptics.conf добавляем строки:
Option "HorizScrollDelta" "-111"
так чтобы секция выглядела следующим образом:
Section “InputClass”Identifier “touchpad catchall”
Driver “synaptics”
MatchIsTouchpad “on”
Option “VertScrollDelta” “-111”
Option “HorizScrollDelta” “-111”
MatchDevicePath “/dev/input/event*”
EndSection
среда, 13 ноября 2013 г.
"Вечный Service" в Android
Когда мы делаем приложения, мы руководствуемся самыми гумаными соображениями. Мы хотим облегчить жизнь нашим клиентам, помочь им, защитить, вооружить против любых проблем этого ужасного мира. Но клиенты почему-то не хотят покоряться нашему мудрому руководству. Закрывают наше приложения, останавливают его, находят в списке "запущенных процессов" через настройки и опять-таки останавливают. Ну, как дети малые, ей-богу ;) Если вы в своей всеобъемлющей мудрости хотите спасти своих клиентов от их самих, вам наверняка прийдётся защититься от их жалких попыток упавлять своим смартфоном. Как же это сделать? Перенести логику в Service, чтобы закрытие приложения не останавливало его работу? Недостаточно. Сделать в нём вечный цикл, вызвать startService() в onDestroy() - тоже. В конце концов эти неразумные существа могут перезагрузить смартфон и мы утратим над ними власть не сможем помогать им. Можно, конечно ловить событие загрузки (и ещё стопицот других системных событий) и поднимать наш сервис. Однако система на то и система, чтобы жить своими, непредсказуемыми событиями, а значит никакой гарантии, что эти события произойдут когда нам нужно, увы, нет. Как же нам быть? Ответ очевиден:
среда, 6 ноября 2013 г.
UDP в Android: как приложения ищут друг друга в сети?
В большинстве случаев говря о передаче данных по сети мы имеем в виду TCP. Для большинства сетевых задач этот протокол лучший, но он вообще-то совсем не единственный. И есть вещи, которые с его помощью делать не удобно. Например: мы знаем порт, но не знаем IP-адреса получателя и нам нужно его найти. Опрашивать все адреса локальной сети по очереди? Жуть... Тут надо бы послать широковещательное сообщение, а для этого мы уже используем UDP. Этот протокол достаточно интересен. Например, мы можем слать сообщения и слушать на одном и том же проту одновременно. UDP при отправке широковещательных сообщений не создаёт соединения в привычном нам смысле. Мы не знаем, доставлено ли получателю наше UDP сообщение. Впрочем нам это и не нужно. Передавать данные мы будем уже по TCP. Итак как же экземплярам нашего приложения искать друг друга?
среда, 30 октября 2013 г.
Java EE: Удобные логи - это просто
- Привет, тут у меня клиент, он не видит в архиве своих операций. Глянь плиз, что у него не так? Только быстрее, он VIP, он злой и висит на линии...
Если вы пишите серверные приложения, то такие просьбы девочек из саппорта вам знакомы. И знакомо то чувство отвращения, злости и отчаяния с которым вы бросаете свой незаконченный рефакторинг в самом неподходящем месте, чтобы залезть в консоль и смотреть логи.
А логи, как на зло пишутся по старинке, потому что разбираться с log4j некогда, да и неохота... Вот если бы сливать логи в базу, да запилить админку... Но когда этим заниматься? 100500 задач в разработке а тут ещё эти логи.
Как же быть? Выделить день-другой и переписать логирование на какой-нибудь фреймворк? Это можно. Но мы ведь программисты, давайте сначала оценим задачу. Итак чего бы нам хотелось?
Если вы пишите серверные приложения, то такие просьбы девочек из саппорта вам знакомы. И знакомо то чувство отвращения, злости и отчаяния с которым вы бросаете свой незаконченный рефакторинг в самом неподходящем месте, чтобы залезть в консоль и смотреть логи.
А логи, как на зло пишутся по старинке, потому что разбираться с log4j некогда, да и неохота... Вот если бы сливать логи в базу, да запилить админку... Но когда этим заниматься? 100500 задач в разработке а тут ещё эти логи.
Как же быть? Выделить день-другой и переписать логирование на какой-нибудь фреймворк? Это можно. Но мы ведь программисты, давайте сначала оценим задачу. Итак чего бы нам хотелось?
- Логи не должны писаться вперемешку. Все записи в контексте одного запроса должны в логах находиться в одном месте, "пакетом".
- Все записи одного уровня должны маркироваться общим тегом
- Вместе с пакетом записей должна быть возможность вывести в лог произвольные данные (например id клиента и т.п.), относящиеся к этому запросу.
И всё? И для этих трёх простых вещей тащить в свой проект ещё один фреймврок? Нет уж, давайте напишим несколько строк кода и покончим с этим ;)
вторник, 29 октября 2013 г.
Настраиваем связку Tomcat+Nginx
Это сугубо практичный пост: просто набор команд для настройки "маленького сервера с блекджеком и танцовщицами" для хостинга java-приложений. Настраивал сегодня wds на Ubuntu 12.04 и, наверное, в десятый раз вспоминал всё это. Теперь всё будет в одном месте.
sudo apt-get update
sudo apt-get install oracle-java6-installer
2. Tomcat
1. Java
sudo add-apt-repository ppa:webupd8team/javasudo apt-get update
sudo apt-get install oracle-java6-installer
в конец ~/.bashrc добавляем
export JAVA_HOME=/usr/lib/jvm/java-6-oracle
2. Tomcat
sudo apt-get install tomcat7
Tomcat будет стартовать при старте системы, приложения будут разворачиваться на порту 8080 (каждое в своём соответствующем контексте) если .war залить в /var/lib/tomcat7/webapps/.
Так уже можно работать, но для клиентов нужно спрятать tomcat за прокси, чтобы иметь возможность фровардить порты и прятать контексты за доменными именами.
3. Nginx
wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key
В конец файла /etc/apt/sources.list добавляем строки:
deb http://nginx.org/packages/ubuntu/ codename nginx
deb-src http://nginx.org/packages/ubuntu/ codename nginx
где codename - имя дистрибутива (посмотрите его в строчках выше или получите из сat /etc/*-release)
sudo apt-get update
sudo apt-get install nginx
sudo apt-get install nginx
Это почти всё. Осталось только настроить проксирование запросов. Допустим, мы пишем конфиг для нашего хоста newgoogle.com, приложение для которого мы в tomcat-e развернули на контексте /ng:
sudo cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/newgoogle.com.conf
sudo vim /etc/nginx/conf.d/newgoogle.com.conf
меняем server_name на newgoogle.com и меняем location-секцию на:
location / {
proxy_pass http://localhost:8080/ng/;
proxy_set_header X-Real-IP $remote_addr;
}
P.s.: Ничего сверхестественного тут нет, любой админ сделает вам это одним пальцем с закрытыми глазами после восьми банок пива. Но зачем его беспокоить? То, что описано тут - достаточно тривиально, и сделав это однажды просто повторяйте всегда при необходимости точно также. Пусть гуру-админы занимаются тюнингом наших серверов и тонкой настройкой сетевого трафика. Простые вещи делаем сами.
среда, 9 октября 2013 г.
Авторизация в web-приложениях. Заменяем Spring Security на 100 строк своего кода :)
Есть несколько принципов разработки приложений, вроде бы верных и абстрактно полезных. Эти принципы настолько укоренились в сознании современного программиста, что порой заменяют ему здравый смысл, лишают его способности, которая так ценна в его профессии: способности думать. Вот к примеру:
"Нельзя делать свои велосипеды! Надо использовать проверенные и отлаженные библиотеки!"
Вроде бы верно. Но если посмотреть чуть глубже... Почему так? Адепты "проверенных библиотек" с горящими глазами скажут что-то вроде: "Свои велосипеды писать дольше, их надо отлаживать и их труднее сопровождать". Для "сферического проекта в вакууме" это верно. Но давайте рассмотрим задачу реальную. Я постараюсь поставить условия задачи, которые чаще всего встречались в моей, уже почти 10-летней практике. И покажу решение, которое писать быстрее, проще и легче сопровождать, хотя оно и является классическим "велосипедом" :)
пятница, 4 октября 2013 г.
Создаём свои всплыващие сообщения в Android
среда, 2 октября 2013 г.
Сериализация и десериализация объектов
Программисты пишут код для того, чтобы решить какую-то задачу. Тот или другой способ они выбирают в зависимости от проблем, которые приходится преодолевать на пути к решению. Чаще всего проблемы видны сразу:
- "...мы планируем обслуживать 10 миллионов клиентов к концу первого года." - Пишем с оглядкой на масштабирование.
- "...нужно быстро внедрять 100500 похожих фич." - Думаем над системой плагинов.
- И т.п.
Однако есть проблемы, которые вроде как и не заметны поначалу. Но потом, спустя год активной доработки проекта мы вдруг увязаем в них как в болоте. Одну такую я встретил недавно и цепочка моих рассуждений при её решении показалась мне достаточно интересной, чтобы оформить её тут. В конце этой цепочки - довольно элегантное на мой взгляд решение, которое позволит реализовать специфическую сериализацию объектов в вашем проекте. Это не открытие, подобный подход используется, например, в Parcelable в Android. Но паттерн, до которого дошёл "от проблемы" а не путём изучения научных трудов, запоминается намного лучше.
И, кстати, если вы пришли сюда скопировать исходничек не вникая в текст, лучше почитайте другие посты. Тут много букв, картинок и вообще, скукотища ;)
И, кстати, если вы пришли сюда скопировать исходничек не вникая в текст, лучше почитайте другие посты. Тут много букв, картинок и вообще, скукотища ;)
пятница, 7 июня 2013 г.
Делаем собственный компонент для Android-приложений
Если вы уже не в первый раз делаете приложения под Android, то наверняка вас часто посещает чувство дежавю: решение большинства задач вы уже где-то видели, когда-то что-то делали, а порой вам даже удаётся найти старый код и "вклеить" его в новый проект. Если же вы работаете в команде, то "помню я это где-то делал" уже не работает. Нужно систематизировать наработки, держать актуальный репозиторий библиотек. А когда своих и сторонних библиотек становится много, то поневоле начинаешь перебирать их, оставляя те, что лучше и избавляясь от остальных. Как же понять, что лучше? И как сделать такой компонент, который навсегда займёт почётное место в репозитории вашей команды?
Ниже я предложу своё видение критериев оценки библиотек и в качестве бонуса: код весьма полезного и удобного компонента для асинхронной загрузки, кеширования и отображения картинок из сети.
пятница, 26 апреля 2013 г.
Обучаем Tesseract
Tesseract - свободная платформа для оптического распознавания текста, исходники которой Google подарил сообществу в 2006 году. Если вы пишите софт для распознавания текста, то вам наверняка приходилось обращаться к услугам этой мощной библиотеки. И если она не справилась с вашим текстом, то выход у вас остаётся один - научить её. Процесс этот достаточно сложный и изобилует не очевидными а порой и прям-таки магическими действиями. Оригинальное описание есть тут. Мне понадобился почти целый день на постижение всей его глубины, поэтому тут я хочу сохранить, надеюсь, более понятный его вариант. Так чтобы помочь себе и другим пройти этот путь в следующий раз быстрее.
понедельник, 15 апреля 2013 г.
Шифрование с открытым ключом в java
Давным-давно я как-то описывал несколько примеров использования различных алгоритмов шифрования в java. Решил вот продолжить полезную тему и описать простой пример реализации шифрования алгоритмом RSA.
В отличие от описанных ранее, этот алгоритм асимметричный: т.е. данные шифруются одним ключом (публичным) а расшифровываются другим (приватным).
Есть много преимуществ такого алгоритма перед симметричными, и одно из главных - нем не нужен защищённый канал для передачи секретного ключа. Публичный ключ из своей пары мы можем разместить где угодно ни о чём не беспокоясь: этим ключом расшифровать адресованные нам сообщения в принципе невозможно. И наоборот, наш "собеседник" также свободно распространяет свой публичный ключ и мы можем передавать ему свои сообщения не опасаясь, что их прочитает кто-нибудь посторонний.
Ключи для обмена данными обычно генерируются один раз. После генерации их можно сохранить в файлы. При установке соединения с кем-то мы сохраняем его публичный ключ и передаём ему свой. Если получено шифрованное сообщение, мы восстанавливаем свой приватный ключ и с его помощью выполняем расшифровку.
В отличие от описанных ранее, этот алгоритм асимметричный: т.е. данные шифруются одним ключом (публичным) а расшифровываются другим (приватным).
Есть много преимуществ такого алгоритма перед симметричными, и одно из главных - нем не нужен защищённый канал для передачи секретного ключа. Публичный ключ из своей пары мы можем разместить где угодно ни о чём не беспокоясь: этим ключом расшифровать адресованные нам сообщения в принципе невозможно. И наоборот, наш "собеседник" также свободно распространяет свой публичный ключ и мы можем передавать ему свои сообщения не опасаясь, что их прочитает кто-нибудь посторонний.
Ключи для обмена данными обычно генерируются один раз. После генерации их можно сохранить в файлы. При установке соединения с кем-то мы сохраняем его публичный ключ и передаём ему свой. Если получено шифрованное сообщение, мы восстанавливаем свой приватный ключ и с его помощью выполняем расшифровку.
пятница, 1 февраля 2013 г.
Создание websocket-приложения на Tomcat 7
Ранее я писал о своём опыте использования технологии websocket в связывании серверного java-приложения и android-клиента. Я предлагал использовать jetty 8 и делал обычное консольное java-приложение. Но если у нас websocket является частью web-приложения с сервлетами и т.п., то такой подход уже не годится. Да и зачем тянуть в свой проект jetty, если наш старый добрый Tomcat с версии 7.0.27 уже поддерживает websocket самостоятельно?
Сегодня мы сделаем websocket-приложение с помощью одного только Tomcat и javascript на клиенте.
WebSocketServlet
Ключевым механизмом реализации websocket в Tomcat является класс org.apache.catalina.websocket.WebSocketServlet. Он расширяет обычный javax.servlet.http.HttpServlet а значит мы можем "мапить" его на url как это делаем с остальными своими сервлетами. Собственно сама наша реализация крайне проста:
Мы тут реализуем метод, который возвращает объект, отвечающий за обслуживание websocket-соединения. Сам объект WsConnection выглядит так:
Тут мы расширяем MessageInbound, что позволяет нам переопределяя соответствующие методы обрабатывать события установления и обрыва соединения, получения сообщения и т.п. При подключении нового клиента мы добавляем его в синхронизированную очередь, при отключении - удаляем. При получении сообщения просто рассылаем его всем подключенным клиентам. Такой вот чатик :)
На клиентской стороне можно сделать много красивостей, но мы обойдёмся минимальным количеством кода. Только чтобы убедиться в работоспособности сервера.
Сегодня мы сделаем websocket-приложение с помощью одного только Tomcat и javascript на клиенте.
WebSocketServlet
Ключевым механизмом реализации websocket в Tomcat является класс org.apache.catalina.websocket.WebSocketServlet. Он расширяет обычный javax.servlet.http.HttpServlet а значит мы можем "мапить" его на url как это делаем с остальными своими сервлетами. Собственно сама наша реализация крайне проста:
import org.apache.catalina.websocket.StreamInbound; import org.apache.catalina.websocket.WebSocketServlet; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; @WebServlet(name = "WsServlet", urlPatterns = {"/ws"}) public class WsServlet extends WebSocketServlet { @Override protected StreamInbound createWebSocketInbound(String s, HttpServletRequest httpServletRequest) { return new WsConnection(); } }
Мы тут реализуем метод, который возвращает объект, отвечающий за обслуживание websocket-соединения. Сам объект WsConnection выглядит так:
import org.apache.catalina.websocket.MessageInbound; import org.apache.catalina.websocket.WsOutbound; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.concurrent.ArrayBlockingQueue; public class WsConnection extends MessageInbound { public static ArrayBlockingQueue<WsOutbound> connections = new ArrayBlockingQueue<WsOutbound>(100); private WsOutbound outbound; @Override protected void onBinaryMessage(ByteBuffer byteBuffer) throws IOException { } @Override protected void onTextMessage(CharBuffer charBuffer) throws IOException { broadcast(charBuffer.toString()); } @Override protected void onOpen(WsOutbound outbound) { this.outbound = outbound; connections.add(outbound); } @Override protected void onClose(int status) { connections.remove(this.outbound); } private void broadcast(String message) { for (WsOutbound connection : connections) { try { CharBuffer buffer = CharBuffer.wrap(message); connection.writeTextMessage(buffer); } catch (IOException e) { e.printStackTrace(); } } } }
Тут мы расширяем MessageInbound, что позволяет нам переопределяя соответствующие методы обрабатывать события установления и обрыва соединения, получения сообщения и т.п. При подключении нового клиента мы добавляем его в синхронизированную очередь, при отключении - удаляем. При получении сообщения просто рассылаем его всем подключенным клиентам. Такой вот чатик :)
На клиентской стороне можно сделать много красивостей, но мы обойдёмся минимальным количеством кода. Только чтобы убедиться в работоспособности сервера.
<html> <head> <title></title> <script type="text/javascript"> var socket = new WebSocket("ws://mydomain.com:8080/ws"); socket.onopen = function() { alert("Соединение установлено."); }; socket.onclose = function(event) { if (event.wasClean) { alert('Соединение закрыто'); } else { alert('Обрыв соединения'); } alert('Код: ' + event.code + ' причина: ' + event.reason); }; socket.onmessage = function(event) { var logarea = document.getElementById("log"); logarea.value = event.data+"n"+logarea.value; }; socket.onerror = function(error) { alert("Ошибка " + error.message); }; function send() { var s = document.getElementById("in").value; socket.send(s); } </script> </head> <body> <input type="text" id="in" /><input type="button" onclick="send()" value="send" /> <br/> <textarea id="log" rows="8" cols="20">
среда, 2 января 2013 г.
Устанавливаем sun java в Linux Mint 14
В большинстве Linux-дистрибутивов первое, что мне приходится делать после установки - заменять OpenJDK на "нормальную" java. Не то чтобы я был против лицензионно-чистого софта, просто большинство моих инструментов написаны так, что с OpenJDK ведут себя странно. Обычно процесс замены java на моей машине достаточно однообразен и нетехнологичен: скачать, распаковать, сделать update-alternatives. Но сегодня я таки решил сделать всё правильно:
Инсталлятор
sudo
add-apt-repository ppa:webupd8team/java
sudo
apt-get update
sudo
apt-get
install
oracle-java6-installer
Инсталлятор
сам обновляет alternatives, так что больше ни о чём заботиться не нужно. OpenJDK в системе остаётся, но по умолчанию используется теперь именно java от Oracle. Из этого же репозитория, само собой, можно поставить и 7 и 8 JDK, если они вам больше нравятся.
Подписаться на:
Сообщения (Atom)