barbitoff programmer`s blog

Здесь я публикую заметки из программерской жизни: грабли, на которые мне случилось наступить, проблемы, для которых было найдено элегантное (или не очень) решение, а также все, с чем мне пришлось столкнуться и чем хотелось бы поделиться =)
PS Если хотите меня поблагодарить - на странице есть 3 места, чтобы это сделать =)

пятница, 28 октября 2016 г.

PostgreSQL: просмотр активных запросов и их принудительное завершение

SELECT query, pid FROM pg_stat_activity WHERE state = 'active'
SELECT pg_terminate_backend(<pid из выдачи предыдущего запроса>)

среда, 26 октября 2016 г.

Java: парсинг дат сразу по нескольким шаблонам

Задача

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

Решение

Конечно, можно использовать несколько SimpleDateFormat и цепочку из try-catch, т.е. пробовать распарсить с использованием очередного формата, ловить ParseException, после чего пробовать следующий формат. Но можно воспользоваться библиотекой commons-lang от Apache, ниже приведен пример ее использования на Groovy:
import org.apache.commons.lang.time.DateUtils;
def sourceFormats = [
"yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy-MM-dd'T'HH:mm:ss",
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
"yyyy-MM-dd'T'HH:mm:ssXXX"
];;
sourceFormats = sourceFormats.toArray(new String[1]); // convert ArrayList to String Array
def date = DateUtils.parseDate(stringValue, sourceFormats);

PostgreSQL: генерация дат от определенной даты до текущей

Задача

В PostgreSQL необходимо сгенерировать строки с датами от определенной даты (допустим, это значение MIN() по определенному полю некоторой таблицы) до текущей даты.

Решение

На помощь приходит функция generate_series, которая может генерировать строки как с целочисленными значениями, так и со значениями типа timestamp:
SELECT generate_series(MIN(my_date)::timestamp, CURRENT_TIMESTAMP, '1 day')::date FROM mytable

пятница, 21 октября 2016 г.

Java: Выгрузка в файлы бинарных массивов из хип-дампа

Задача

Выгрузить в файлы все бинарные массивы с объемом больше 10Мб из хип-дампа.

Решение

OQL-скрипт:

var i = 0;
map(
  heap.objects('byte[]', false, 'sizeof(it)>10000000'),
  function(v) {
    var file = new java.io.FileOutputStream("T:/binaries/"+i+".bin");
    try {    
      for(var j=0;j<v.length;j++) {
        file.write(v[j]);
        file.flush();
      }
    } finally {
      file.close();
    }
    i++;
    return v;
  }
);
А вообще, экспортировать массивы байт в файл умеет Netbeans, правда, только по одному массиву за раз. Для этого необходимо выделить нужный массив в списке экземпляров, и нажать "Сохранить в файл" под окошком с заголовком "Элементы массива". В качестве выходного формата поддерживается как csv, так и bin (последний наиболее удобен, если нас интересует непосредственно бинарный контент, а не элементы массива байт по-отдельности). И, кстати, экспорт в файл Natbeans делает на пару порядков быстрее, чем приведенный выше скрипт.

четверг, 20 октября 2016 г.

OQL: запись данных из хип-дампа в файл

Бывают ситуации, когда средств OQL недостаточно для проведения требующегося анализа хип-дампа, и проще выгрузить нужные данные в файл и проанализировать их внешним инструментом. 
К примеру, столкнулся со следующей задачей: есть некий класс со строковым полем. В хип-дампе около 10 миллионов объектов данного класса, и нужно по ним собрать статистику, какое значение данного строкового поля встречается сколько раз. В OQL нет аналога SQL-ного GROUP BY, поэтому, поломав голову над реализацией группировки с применением комбинаций map / sort / filter и упершись в то, что полученное решение работает крайне медленно, я решил выгрузить все строки в файл, и потом уже обработать этот файл каким-нибудь более удобным, чем OQL, инструментом.
Например, следующий код выведет в файл namespaces.log пространства имен родительских элементов всех объектов класса OMTextImpl:
var file = new java.io.FileWriter("T:/namespaces.log");
unique(map(
  heap.objects('org.apache.axiom.om.impl.llom.OMTextImpl'),
  function(v) {
    var ns = v.parent == null ? "null_parent" :(v.parent.ns == null ? "null_ns" : (v.parent.ns.uri == null ? "null_uri" : v.parent.ns.uri));
    file.write(new java.lang.String(ns.toString() + "\r\n"));
    file.flush();
    return "1";
  }
));
А такой код - локальные имена всех объектов OMElementImpl:
var file = new java.io.FileWriter("T:/omelements.log");
unique(map(
  heap.objects('org.apache.axiom.om.impl.llom.OMElementImpl'),
  function(v) {
    file.write(new java.lang.String(v.localName));
    file.write(new java.lang.String("\r\n"));
    file.flush();
    return "1";
  }
)); 
Зачем нужен unique и return "1": дело в том, что, по крайней мере, Netbeans считает, что если OQL вернул 100 объектов, нужно прекратить итерацию, вывести эти 100 объектов пользователю и написать, что, де, "слишком много результатов, уточните запрос".  Т.е. если из запросов выше убрать unique, в файл будут записаны значения только из первых 100 найденных объектов заданного класса. Использование же unique заставит OQL-движок продолжать итерации, пока не будет найдено 100 уникальных значений, а т.к. map-функция всегда возвращает константу "1", это эквивалентно итерации по абсолютно всем объектам.
PS Хорошая справка по OQL: http://visualvm.java.net/oqlhelp.html

вторник, 18 октября 2016 г.

WSO2 ESB 4.9.0: настройка числа слушателей JMS-очереди

Проблема

Есть несколько прокси-сервисов, слушающих различные JMS-очереди (в качестве JMS-сервера используется Apache ActiveMQ). В каждом прокси-сервисе число слушателей настроено через соотв. параметры:
<parameter name="transport.jms.ConcurrentConsumers">25</parameter>
<parameter name="transport.jms.MaxConcurrentConsumers">25</parameter>
Однако, в действительности при большой интенсивности поступления сообщений в очереди шина ведет себя достаточно странно. Суммарное число слушателей всех очередей никогда не превосходит 20, причем эти 20 слушателей иногда распределяются между очередями совершенно неприемлемым образом: одна из очередей остается вообще без слушателей (несмотря на поступление в нее сообщений), а все доступные слушатели переключаются на другую очередь, в которую интенсивно поступают сообщения.

Решение

Максимальное число слушателей для очередей ограничивается не только соотв. параметром, задаваемым на уровне прокси-сервиса. Во-первых, его можно настроить на глобально для всего JMSListener'а на уровне axis2.xml:
<transportReceiver name="jms" class="org.apache.axis2.transport.jms.JMSListener">
 <parameter name="MYJMS" locked="false">
  <parameter name="java.naming.factory.initial" locked="false">
    org.apache.activemq.jndi.ActiveMQInitialContextFactory
  </parameter>
  <parameter name="java.naming.provider.url" locked="false">
    tcp://localhost:61616?jms.prefetchPolicy.queuePrefetch=0
  </parameter>
  <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">
    QueueConnectionFactory
  </parameter>
  <parameter name="transport.jms.ConnectionFactoryType" locked="false">
    queue
  </parameter>
  <parameter name="transport.jms.ConcurrentConsumers" locked="false">
    50
  </parameter>
  <parameter name="transport.jms.MaxConcurrentConsumers" locked="false">
    50
  </parameter>

 </parameter>
</transportReceiver>
Задаваемые здесь ограничения имеют приоритет над указываемыми на уровне прокси-сервиса. Но в данном случае в axis2.xml никаких ограничений прописано не было, так что проблема была в другом. JMS-слушатели формируются из ограниченного пула "server workers", размер которого настраивается системными свойствами snd_t_core и snd_t_max. Значение по-умолчанию равно тем самым 20, в которые мы и упираемся. Прописываем в wso2seerver.sh параметр -Dsnd_t_max=<N>, где N нужно выбрать таким образом, чтобы сумма значений transport.jms.MaxConcurrentConsumers, заданных во всех прокси-сервисах, слушающих JMS-очереди, не превышала этого значения.