<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6847658947227037305</id><updated>2025-12-20T11:00:40.779+03:00</updated><category term="Программирование"/><category term="Java"/><category term="Linux"/><category term="администрирование"/><category term="Eclipse"/><category term="bash"/><category term="красиво"/><category term="web"/><category term="Ant"/><category term="KDE"/><category term="network"/><category term="openSUSE"/><category term="Базы данных"/><category term="Apache Tomcat"/><category term="BUGS"/><category term="CMS"/><category term="GWT"/><category term="Google"/><category term="NVIDIA"/><category term="ODF"/><category term="OpenOffice.org"/><category term="kdesu"/><category term="libpcap"/><category term="Виртуализация"/><category term="гадость"/><category term="Apache Derby"/><category term="AppEngine"/><category term="CUDA"/><category term="D-Bus"/><category term="GTK"/><category term="JDK"/><category term="Magnolia"/><category term="MongoDB"/><category term="ODFDOM"/><category term="ProcessBuilder"/><category term="Redmine"/><category term="Reports"/><category term="Soft"/><category term="VMware"/><category term="Web-камеры"/><category term="Wi-Fi"/><category term="XML"/><category term="cat"/><category term="d-link"/><category term="firefox"/><category term="freenx-server"/><category term="iptables"/><category term="javaplugin"/><category term="jdom"/><category term="kdialog"/><category term="kvm"/><category term="messengers"/><category term="nxserver"/><category term="qdbus"/><category term="qutIM"/><category term="server.xml"/><category term="spice"/><category term="udev"/><category term="udevadm"/><category term="update-alternatives"/><category term="wave"/><category term="www.blogger.com"/><category term="Данные"/><category term="Кодировка"/><category term="Сервлеты"/><title type='text'>По пути Java, Linux и Open Source</title><subtitle type='html'>Блог о Java, Linux и различных Open Source технологиях</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default?start-index=26&amp;max-results=25'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>40</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-5630676282349539811</id><published>2016-06-28T14:48:00.000+03:00</published><updated>2016-07-08T14:16:03.994+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="libpcap"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="Программирование"/><category scheme="http://www.blogger.com/atom/ns#" term="администрирование"/><title type='text'>Как отсыпать совсем немножечко рута для Java и разрешить захватывать сетевые пакеты</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
В различных дистрибутивах Linux такие программы, как &lt;b&gt;tcpdump&lt;/b&gt; и &lt;b&gt;wireshark&lt;/b&gt;, использующиеся для анализа сетевого трафика, обычно требуют прав суперпользователя для получения доступа к возможностям захвата пакетов. Точно также дело обстоит и с Java-программами, которые используют системную библиотеку &lt;b&gt;libpcap&lt;/b&gt;. Причем в этом случае вам придется запустить с неограниченными правами саму виртуальную машину Java, которая уже в свою очередь будет исполнять ваш код. Я думаю, мало кто любит лишний раз сорить суперправами направо и налево, поэтому в Linux, начиная с ядра 2.2 появилась такая штука, как &lt;b&gt;Linux Capabilites&lt;/b&gt;, которая позволяет наделить непривилегированные процессы только необходимыми возможностями суперпользователя. В данном случае, например, нас интересует только возможность перехвата и отправки сетевых пакетов. Для установки и просмотра Linux Capabilites необходимы программы &lt;b&gt;setcap&lt;/b&gt; и &lt;b&gt;getcap&lt;/b&gt;, соответственно. У меня в openSUSE эти утилиты находятся в пакете &lt;b&gt;libcap-progs&lt;/b&gt;, а в Ubuntu в пакете &lt;b&gt;libcap2-bin&lt;/b&gt;.&lt;/div&gt;
&lt;br /&gt;
Установка возможности захвата и отправки сетевых пакетов и возможности изменения конфигурации интерфейсов выглядит так:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;setcap cap_net_raw,cap_net_admin=eip /usr/local/lib64/jdk1.8.0_77/bin/java&lt;/span&gt;&lt;/blockquote&gt;
Просмотр установленных возможностей:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;getcap /usr/local/lib64/jdk1.8.0_77/bin/java&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;/usr/local/lib64/jdk1.8.0_77/bin/java = cap_net_admin,cap_net_raw+eip&lt;/span&gt; &lt;/blockquote&gt;
Всё просто, но если после установки этих возможностей попробовать запустить программу java из под обычного пользователя, то это не очень получится:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;java -version&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directory&lt;/span&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Это происходит из-за того, что при наличии у непривилегированного процесса возможностей, которыми он вроде как обладать не должен, загрузчик динамических библиотек запрещает загрузку библиотек из недоверенных каталогов. Доверенными каталогами по умолчанию являются &lt;b&gt;/lib&lt;/b&gt; и &lt;b&gt;/usr/lib&lt;/b&gt; (а также &lt;b&gt;/lib64&lt;/b&gt; и &lt;b&gt;/usr/lib64&lt;/b&gt;).&lt;/div&gt;
&lt;br /&gt;
С помощью утилиты &lt;b&gt;ldd&lt;/b&gt; посмотрим, какие динамические библиотеки использует java:&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;ldd /usr/local/lib64/jdk1.8.0_77/bin/java&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; linux-vdso.so.1 (0x00007ffc91ffd000)&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libpthread.so.0 =&amp;gt; /lib64/libpthread.so.0 (0x00007fc3dc124000)&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libjli.so =&amp;gt; /usr/local/lib64/jdk1.8.0_77/bin/../lib/amd64/jli/libjli.so (0x00007fc3dbf0d000)&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libdl.so.2 =&amp;gt; /lib64/libdl.so.2 (0x00007fc3dbd09000)&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libc.so.6 =&amp;gt; /lib64/libc.so.6 (0x00007fc3db961000)&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /lib64/ld-linux-x86-64.so.2 (0x00005584e041d000)&lt;/span&gt; &lt;/span&gt;&lt;/blockquote&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Как видно из вывода команды &lt;b&gt;ldd&lt;/b&gt;, при запуске java не удалось загрузить как раз ту самую библиотеку, которая находится в недоверенном каталоге &lt;b&gt;../lib/amd64/jli/&lt;/b&gt;.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Это можно легко пофиксить, добавив файл с расширением &lt;b&gt;.conf&lt;/b&gt;&amp;nbsp; в каталог &lt;b&gt;/etc/ld.so.conf.d&lt;/b&gt;, в котором перечислить все необходимые пути. В случае с java это всего лишь один каталог:&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;echo /usr/local/lib64/jdk1.8.0_77/lib/amd64/jli &amp;gt; /etc/ld.so.conf.d/java.conf&lt;/span&gt;&lt;/blockquote&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
После изменения файлов в /etc/ld.so.conf.d может потребоваться перестроить кэш библиотек, для этого нужно выполнить:&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;ldconfig&lt;/span&gt;&lt;/blockquote&gt;
Проверяем, что загрузчик видит нашу библиотеку:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;ldconfig -v |grep jli&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;/usr/local/lib64/jdk1.8.0_77&lt;/span&gt;&lt;/span&gt;/lib/amd64/jli:&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libjli.so -&amp;gt; libjli.so&lt;/span&gt; &lt;/blockquote&gt;
Ну и на всякий случай, команда для удаления всех дополнительных возможностей у программы:&lt;br /&gt;
&lt;blockquote&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt; &lt;/span&gt; &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;setcap -r /usr/local/lib64/jdk1.8.0_77/bin/java&lt;/span&gt;&lt;/blockquote&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Ещё хотелось бы отметить, что не стоит без лишней необходимости добавлять нестандартные возможности вашей глобальной системной Java-машине, потому что в этом случае любая запущенная Java-программа сможет ими воспользоваться. Для разработки и запуска программ с завышенными потребностями лучше завести отдельный экземпляр Java-машины.&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Где отрыто&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&lt;a href=&quot;http://packetlife.net/blog/2010/mar/19/sniffing-wireshark-non-root-user/&quot;&gt;Как приструнить Wireshark&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&lt;a href=&quot;http://bugs.java.com/view_bug.do?bug_id=7157699&quot;&gt;Небаг в багтрекере Java&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;Куришка: &lt;b&gt;man capabilities&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/5630676282349539811/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2016/06/linux-capabilites-java.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/5630676282349539811'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/5630676282349539811'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2016/06/linux-capabilites-java.html' title='Как отсыпать совсем немножечко рута для Java и разрешить захватывать сетевые пакеты'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-518534850265845169</id><published>2016-03-28T09:09:00.000+03:00</published><updated>2017-04-07T00:05:56.878+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="JDK"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="update-alternatives"/><category scheme="http://www.blogger.com/atom/ns#" term="администрирование"/><title type='text'>Установка Java Development Kit в Linux из архива tar.gz</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;Данный пост, это просто обновление поста 2010 года, который до сих пор пользуется довольно большой популярностью, а так как с тех времен уже немало воды утекло, то я посчитал своим долгом актуализировать эту информацию. По пути также кое-что было переработано, надеюсь, в сторону понятности.&lt;/span&gt;&lt;/blockquote&gt;
Если вам потребовался стандартный &lt;b&gt;JDK&lt;/b&gt;  от &lt;b&gt;Oracle&lt;/b&gt;, вместо &lt;b&gt;OpenJDK&lt;/b&gt;,  который поставляется с современными свободными дистрибутивами &lt;b&gt;Linux&lt;/b&gt;, то  его можно скачать либо с дополнительных сторонних репозиториев для  вашего дистрибутива, либо напрямую с сайта&amp;nbsp;&lt;a href=&quot;http://www.oracle.com/technetwork/java/javase/downloads/index.html&quot;&gt;www.oracle.com&lt;/a&gt;. Если ставить Java  из репозиториев для вашего дистрибутива, то скорее всего всё само  настроется и заработает, и ни о чем беспокоится вам не придется, но если  вы скачали JDK или JRE в виде архива &lt;b&gt;tar.gz&lt;/b&gt; с сайта Oracle, придется совершить несколько дополнительных  телодвижений.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
На данный момент скачать JDK с сайта Oracle можно в двух видах: это RPM-пакет и вышеупомянутый архив tar.gz. Про то, на сколько удачно ставится JDK из RPM-пакета, мне сказать нечего - не пробовал. Я предпочитаю устанавливать JDK именно из архива, потому что так я получаю полное управление и понимание того, что и откуда у меня будет запускаться, а также универсальное решение, которое подойдет для всех дистрибутивов, а не только для RPM-based.&lt;/div&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;План действий &lt;/span&gt;&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Скачать нужную версию JDK (архив tar.gz)&lt;/li&gt;
&lt;li&gt;Распаковать архив&lt;/li&gt;
&lt;li&gt;Настроить переменные окружения&lt;/li&gt;
&lt;li&gt;Настроить update-alternatives&lt;/li&gt;
&lt;/ol&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Куда распаковать?&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Итак, скачали JDK. Распаковываем. Но куда? Вроде бы тупой вопрос, но я далеко не сразу пришёл к правильному&amp;nbsp;&lt;u&gt; для себя&amp;nbsp;&lt;/u&gt; решению. Сейчас я все JDK распаковываю в каталог &lt;i&gt;&lt;b&gt;/usr/local/lib64&lt;/b&gt;&lt;/i&gt; и там у меня одновременно живёт несколько версий JDK. Например, на данный момент, там у меня есть каталоги jdk1.7.0_79 и jdk1.8.0_77. В дополнение к этим каталогам, в &lt;i&gt;&lt;b&gt;/usr/local/lib64&lt;/b&gt;&lt;/i&gt; у меня находится симлинк с именем &lt;i&gt;&lt;b&gt;jdk&lt;/b&gt;&lt;/i&gt;, который сегодня чаще всего указывает на каталог jdk1.8.0_77. К такой конфигурации я пришел, когда потребовалось периодически переключаться между различными версиями JDK, причем переключаться нужно было глобально во всей системе. Каким образом такая конфигурация помогает переключаться между версиями, станет понятно ниже.&lt;/div&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Переменные окружения&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Простой  распаковки файлов JDK недостаточно для того, чтобы можно было  нормально использовать установленный JDK по умолчанию в системе.  Следующим шагом необходимо настроить переменные окружения. Для настройки глобальных переменных окружения в Linux используются специальные конфигурационные файлы, обычно это &lt;i&gt;&lt;b&gt;/etc/profile&lt;/b&gt;&lt;/i&gt; (в OpenSUSE предпочтительнее &lt;b&gt;&lt;i&gt;/etc/profile.local&lt;/i&gt;&lt;/b&gt; ).&lt;/div&gt;
&lt;br /&gt;
Ниже приведен пример настройки моих переменных окружения для JDK:&lt;br /&gt;
&lt;br /&gt;
JAVA_HOME=/usr/local/lib64/jdk&lt;br /&gt;
JRE_HOME=$JAVA_HOME/jre&lt;br /&gt;
PATH=$JAVA_HOME/bin:$PATH&lt;br /&gt;
export JAVA_HOME JRE_HOME PATH&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Как можно заметить, переменная JAVA_HOME ссылается у меня не на конкретный реальный каталог с JDK, а на симлинк. Удобство этого подхода для меня заключается в том, что я могу переключать глобальный JDK по умолчанию, просто поменяв симлинк на каталог с JDK необходимой версии.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Примечания:&lt;/div&gt;
&lt;ul style=&quot;text-align: justify;&quot;&gt;
&lt;li&gt;В некоторых особых случая, возможно, понадобится добавить эти же строки в файл&lt;i&gt;&lt;b&gt; /etc/bashrc &lt;/b&gt;&lt;/i&gt;(в &lt;b&gt;SUSE&lt;/b&gt; &lt;b&gt;&lt;i&gt;/etc/bash.bashrc.local&lt;/i&gt;&lt;/b&gt;), который читается &lt;b&gt;bash&lt;/b&gt;&#39;ем, если он запущен без параметра --login &lt;/li&gt;
&lt;li&gt;В  дистрибутивах &lt;b&gt;SUSE&lt;/b&gt; для установки глобальных переменных окружения  по умолчанию следует использовать файлы &lt;b&gt;&lt;i&gt;/etc/profile.local&lt;/i&gt;&lt;/b&gt; и &lt;b&gt;&lt;i&gt;/etc/bash.bashrc.local&lt;/i&gt;&lt;/b&gt;, потому что после апгрейда системы, файлы&lt;i&gt; &lt;/i&gt;&lt;b&gt;&lt;i&gt;/etc/profile&lt;/i&gt;  &lt;/b&gt;и &lt;b&gt;&lt;i&gt;/etc/bash.bashrc &lt;/i&gt;&lt;/b&gt;могут быть перезаписаны. Если таких файлов нет, &lt;u&gt;то  необходимо их создать&lt;/u&gt;. Также, вместо использования файла &lt;i&gt;&lt;b&gt;/etc/profile.local&lt;/b&gt;,&lt;/i&gt; в каталоге&amp;nbsp; &lt;i&gt;&lt;b&gt;/etc/profile.d&lt;/b&gt;&lt;/i&gt; можно создать отдельный bash-скрипт, с командами для настройки окружения JDK. В этом случае, при создании скрипта, важно не пересечься  с именами скриптов, которые могут быть созданы системой в этом же каталоге в будущем при установке каких-либо пакетов.&lt;/li&gt;
&lt;li&gt;Я не знаю аналога &lt;b&gt;&lt;i&gt;/etc/profile.local&lt;/i&gt;&lt;/b&gt; в дистрибутивах&lt;b&gt;&lt;i&gt; &lt;/i&gt;Ubuntu&lt;/b&gt;, поэтому могу порекомендовать использовать каталог &lt;i&gt;&lt;b&gt;/etc/profile.d&lt;/b&gt;&lt;/i&gt;.&lt;i&gt;&lt;b&gt; &lt;/b&gt;&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;Переменная &lt;b&gt;JRE_HOME&lt;/b&gt;  может быть и не особо нужна, но я часто встречаю её в современных  дистрибутивах, поэтому на всякий случай можно её установить - вдруг она понадобится какой-нибудь странной программе :)&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
После изменения файла &lt;b&gt;&lt;i&gt;/etc/profile&lt;/i&gt;&lt;/b&gt;&amp;nbsp;&lt;b&gt;&lt;i&gt;&lt;/i&gt;&lt;/b&gt;(или других подобных файлов, см. примечания выше), необходимо перезайти  в систему. В консоле можно проверить правильно ли установлены значения  переменных:&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
$ set |grep  JAVA_HOME&lt;br /&gt;
JAVA_HOME=/usr/local/lib64/jdk&lt;br /&gt;
$  set |grep ^PATH&lt;br /&gt;
PATH=/usr/local/lib64/jdk/bin:/usr/local/bin:/usr/bin:/bin:/usr/bin/X11&lt;/div&gt;
&lt;br /&gt;
Теперь  проверяем версию Java:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
$ java  -version&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
java  version &quot;1.8.0_77&quot;&lt;/div&gt;
&lt;br /&gt;
В принципе это всё и уже по умолчанию в системе будет использоваться Oracle JDK вместо OpenJDK. Но в современных дистрибутивах лучше выполнить ещё одну настройку, о которой написано ниже.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Настройка update-alternatives&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
На  самом деле, переменную &lt;b&gt;PATH&lt;/b&gt; можно было и не менять. Сегодня в дистрибутивах Linux установка среды выполнения Java по  умолчанию осуществляется с помощью утилиты &lt;b&gt;update-alternatives&lt;/b&gt;.&amp;nbsp;  Кратко опишу принцип её работы на моем примере с java.&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
В каталоге  альтернатив &lt;i&gt;&lt;b&gt;/etc/alternatives&lt;/b&gt;&lt;/i&gt; есть  символическая ссылка java, которая указывает на настоящий файл java,  расположенный где-то в файловой системе. В каталоге &lt;b&gt;&lt;i&gt;/usr/bin&lt;/i&gt;&lt;/b&gt;  есть символическая ссылка, которая указывает на ссылку &lt;i&gt;&lt;b&gt;/etc/alternatives/java&lt;/b&gt;&lt;/i&gt;.  Таким образом, для всех пользователей и программ, которые вызывают java  на самом деле доступна ссылка &lt;i&gt;&lt;b&gt;/usr/bin/java&lt;/b&gt;&lt;/i&gt;  которая ссылается на реальную программу java через ссылку &lt;i&gt;&lt;b&gt;/etc/alternatives/java&lt;/b&gt;&lt;/i&gt;.  Утилита &lt;b&gt;update-alternatives&lt;/b&gt; служит для переключения  символической ссылки &lt;i&gt;&lt;b&gt;/etc/alternatives/java&lt;/b&gt;&lt;/i&gt; на  различные версии java, установленные в системе, а все пользователи либо  программы как использовали ссылку &lt;i&gt;&lt;b&gt;/usr/bin/java&lt;/b&gt;&lt;/i&gt;  для вызова java, так и будут продолжать её использовать.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Посмотрим,  какие установленные версии java доступны для &lt;b&gt;&lt;i&gt;/etc/alternatives/java&lt;/i&gt;&lt;/b&gt;  в моем случае c помощью параметра &lt;b&gt;--list&lt;/b&gt;, для этого программе update-alternatives также необходимо указать имя ссылки в каталоге &amp;nbsp;&lt;i&gt;&lt;b&gt;/etc/alternatives&lt;/b&gt;&lt;/i&gt;:&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
# update-alternatives --list java&lt;br /&gt;
/usr/lib64/jvm/jre-1.7.0-openjdk/bin/java&lt;br /&gt;
/usr/lib64/jvm/jre-1.8.0-openjdk/bin/java&lt;/div&gt;
&lt;br /&gt;
Используя параметр&amp;nbsp; &lt;b&gt;--config&lt;/b&gt; можно посмотреть доступные  альтернативы для java, а потом выбрать нужную в интерактивном режиме:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
# update-alternatives --config java&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
There are 2 choices for the alternative java (providing /usr/bin/java).&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; Selection&amp;nbsp;&amp;nbsp;&amp;nbsp; Path&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Priority&amp;nbsp;&amp;nbsp; Status&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
* 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /usr/lib64/jvm/jre-1.8.0-openjdk/bin/java&amp;nbsp;&amp;nbsp; 1805&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; auto mode&lt;br /&gt;
&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /usr/lib64/jvm/jre-1.7.0-openjdk/bin/java&amp;nbsp;&amp;nbsp; 1705&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; manual mode&lt;br /&gt;
&amp;nbsp; 2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /usr/lib64/jvm/jre-1.8.0-openjdk/bin/java&amp;nbsp;&amp;nbsp; 1805&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; manual mode&lt;br /&gt;
&lt;br /&gt;
Press enter to keep the current choice[*], or type selection number: &lt;/div&gt;
&lt;br /&gt;
Приведу теперь простую и понятную схему ссылок, которая используется на данный  момент в моей системе:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;&lt;b&gt;/usr/bin/java&lt;/b&gt;&lt;/i&gt;  &lt;b&gt;-&amp;gt;&lt;/b&gt;&lt;b&gt;&lt;i&gt; /etc/alternatives/java&lt;/i&gt;&lt;/b&gt;  &lt;b&gt;-&amp;gt;&amp;nbsp;&lt;/b&gt;&lt;i&gt;&lt;b&gt;/usr/lib64/jvm/jre-1.8.0-openjdk/bin/java&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Итак,  как стало ясно, в моем случае доступно две  альтернативы для java, это OpenJDK 1.7 и OpenJDK 1.8, и сейчас update-alternatives настроены на использование OpenJDK 1.8. Для того, чтобы добавить  альтернативную версию java, необходимо &lt;span style=&quot;font-family: inherit;&quot;&gt;использовать&lt;/span&gt; параметр  &lt;b&gt;--install&lt;/b&gt; и указать следующее: &lt;/div&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li style=&quot;text-align: justify;&quot;&gt;Главную ссылку, которая будет  ссылаться на символическую ссылку в &lt;b&gt;&lt;i&gt;/etc/alternatives &lt;/i&gt;&lt;/b&gt;(для  моего случая это &lt;b&gt;&lt;i&gt;/bin/usr/java&lt;/i&gt;&lt;/b&gt;)&lt;/li&gt;
&lt;li style=&quot;text-align: justify;&quot;&gt;Имя  ссылки в каталоге &lt;i&gt;&lt;b&gt; /etc/alternatives&lt;/b&gt;&lt;/i&gt; (в данном  случае java) &lt;/li&gt;
&lt;li style=&quot;text-align: justify;&quot;&gt;Альтернативу для этой ссылки, т.е. местоположение самой  программы (&lt;i&gt;&lt;b&gt; /usr/local/lib64/jdk/bin/java&lt;/b&gt;&lt;/i&gt;) &lt;/li&gt;
&lt;li style=&quot;text-align: justify;&quot;&gt;Приоритет для этой альтернативы. Приоритет необходим для  автоматического выбора альтернативы при включении &lt;b&gt;automatic mode&lt;/b&gt;  (будет выбрана альтернатива с наибольшим приоритетом).&lt;/li&gt;
&lt;/ol&gt;
Устанавливаем новую альтернативу:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;# update-alternatives --install /usr/bin/java java /usr/local/lib64/jdk/bin/java 10000&lt;br /&gt;
update-alternatives: using /usr/local/lib64/jdk/bin/java to provide /usr/bin/java (java) in auto mode&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
Из сообщения понятно, что теперь у нас установлена новая альтернатива для java, а update-alternatives автоматически переключилась на неё, т. к. при установке мы использовали значение приоритета более высокое, чем у двух альтернатив, которые были настроены в системе ранее. &lt;br /&gt;
&lt;br /&gt;
Посмотрим, что доступно теперь:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;# update-alternatives --list  java&lt;/span&gt;/usr/lib64/jvm/jre-1.7.0-openjdk/bin/java&lt;br /&gt;
/usr/lib64/jvm/jre-1.8.0-openjdk/bin/java&lt;br /&gt;
/usr/local/lib64/jdk/bin/java&lt;/div&gt;
&lt;br /&gt;
После  установки новой альтернативы видим, что в списке появилась альтернатива &lt;i&gt;&lt;b&gt;&lt;i&gt;&lt;b&gt;/usr/local/lib64/jdk/&lt;/b&gt;&lt;/i&gt;bin/java&lt;/b&gt;&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, если нам необходимо переключиться на какую-либо из доступных альтернатив, можно использовать параметр&amp;nbsp; &lt;b&gt;--config&lt;/b&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
#  update-alternatives --config java&lt;/div&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;There are 3 choices for the alternative java (providing /usr/bin/java).&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; Selection&amp;nbsp;&amp;nbsp;&amp;nbsp; Path&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Priority&amp;nbsp;&amp;nbsp; Status&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
* 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /usr/local/lib64/jdk/bin/java&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; auto mode&lt;br /&gt;
&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /usr/lib64/jvm/jre-1.7.0-openjdk/bin/java&amp;nbsp;&amp;nbsp; 1705&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; manual mode&lt;br /&gt;
&amp;nbsp; 2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /usr/lib64/jvm/jre-1.8.0-openjdk/bin/java&amp;nbsp;&amp;nbsp; 1805&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; manual mode&lt;br /&gt;
&amp;nbsp; 3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /usr/local/lib64/jdk/bin/java&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; manual mode&lt;br /&gt;
&lt;br /&gt;
Press enter to keep the current choice[*], or type selection number: 1&lt;br /&gt;
update-alternatives: using /usr/lib64/jvm/jre-1.7.0-openjdk/bin/java to provide /usr/bin/java (java) in manual mode&lt;/span&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Ручное переключение на альтернативу отключило &lt;b&gt;automatic mode&lt;/b&gt;.  Теперь при добавлении еще одной версии java в альтернативы  автоматического переключения не произойдет. Для включения &lt;b&gt;automatic mode&lt;/b&gt; снова, нужно ввести 0 при использовании --config java.&lt;b&gt;&lt;/b&gt;&lt;/div&gt;
&lt;b&gt;&lt;br /&gt;
&lt;/b&gt; Примечания:&lt;br /&gt;
&lt;ul&gt;
&lt;li style=&quot;text-align: justify;&quot;&gt;Важно понимать, что с помощью update-alternatives настраивается только ссылка на исполняемый файл java, а переменная окружения JAVA_HOME остаётся неизменной. Т. к. многим программам может потребоваться корректно установленная переменная JAVA_HOME, то для интеграции Oracle JDK в систему, необходимо использовать комплексный подход, заключающийся в настройках переменных окружения и update-alternatives.&lt;/li&gt;
&lt;li style=&quot;text-align: justify;&quot;&gt;Да, update-alternatives можно использовать и для настройки альтернатив каталога с jdk, вместо приведенного в статье варианта ручного управления симлинком &lt;i&gt;&lt;b&gt;/usr/local/lib64/jdk&lt;/b&gt;&lt;/i&gt;, но по-моему, при добавлении новой версии jdk, гораздо проще поменять руками собственный симлинк, чем запоминать параметры команды update-alternatives для установки новой альтернативы.&lt;/li&gt;
&lt;li style=&quot;text-align: justify;&quot;&gt;При  использовании &lt;b&gt;--config&lt;/b&gt;, символ &quot;*&quot; показывает какая  альтернатива сейчас используется.&lt;/li&gt;
&lt;li style=&quot;text-align: justify;&quot;&gt;Если вы руками измените значение  ссылок &lt;i&gt;&lt;b&gt;/etc/alternatives/java&lt;/b&gt;&lt;/i&gt; или &lt;i&gt;&lt;b&gt;/usr/bin/java&lt;/b&gt;&lt;/i&gt;,  то скорее всего при перезагрузке эти значения вернутся в состояние  которое описано утилитой update-alternatives.&lt;/li&gt;
&lt;li style=&quot;text-align: justify;&quot;&gt;Каталог  альтернатив &lt;i&gt;&lt;b&gt;/etc/alternatives&lt;/b&gt;&lt;/i&gt; является  каталогом по умолчанию и может быть изменен командой &lt;b&gt;update-alternatives  --altdir&lt;/b&gt;.&lt;/li&gt;
&lt;li style=&quot;text-align: justify;&quot;&gt;Утилиту update-alternatives  можно использовать также для создания своих собственных групп  альтернатив.&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
После настройки update-alternatives можно удалить строку в файле &lt;i&gt;&lt;b&gt;/etc/profile&lt;/b&gt;&lt;/i&gt;,  которая добавляет в переменную окружения &lt;b&gt;PATH&lt;/b&gt; путь к  директории &lt;i&gt;&lt;b&gt;$JAVA_HOME/bin&lt;/b&gt;&lt;/i&gt;, если, конечно, вам не нужны в глобальном пространстве другие программы из этого каталога (вроде javac и jvisualvm), и перезайти в  систему. После этого вы будете использовать java из Oracle JDK по  привычной ссылке &lt;i&gt;&lt;b&gt;/usr/bin/java&lt;/b&gt;&lt;/i&gt;.&lt;/div&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/518534850265845169/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2010/04/java-development-kit-jdk-linux-update.html#comment-form' title='Комментарии: 8'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/518534850265845169'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/518534850265845169'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2010/04/java-development-kit-jdk-linux-update.html' title='Установка Java Development Kit в Linux из архива tar.gz'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-4937414447932051194</id><published>2016-02-13T10:58:00.000+03:00</published><updated>2016-02-17T00:44:22.305+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="kvm"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="spice"/><category scheme="http://www.blogger.com/atom/ns#" term="Виртуализация"/><category scheme="http://www.blogger.com/atom/ns#" term="администрирование"/><title type='text'>USB redirection error в Spicy на openSUSE</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;
Столкнулся в openSUSE с тем, что в gtk-клиенте для spice не работает проброс USB-устройств внутрь гостевой системы. После выбора устройства для проброса появляется окно с просьбой ввести пароль суперпользователя. Вводим пароль, жмём OK и получаем сообщение вроде этого:&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
Could not redirect Kingston DataTraveler 3.0 [0951:1666] at 2-13: Error setting USB device node ACL: &#39;Error setting facl: Operation not permitted&#39;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Оказывается, что в пакете &lt;b&gt;spice-gtk&lt;/b&gt; вместе с клиентом spicy есть еще такая программка с длинным названием &lt;b&gt;spice-client-glib-usb-acl-helper&lt;/b&gt;. При подключении USB-устройства через spicy эта программа, по всей видимости, проверяет и назначает файлу этого устройства дополнительные права через &lt;a href=&quot;http://open-suse.ru/opensuse-doc/cha.security.acls.html&quot;&gt;ACL&lt;/a&gt;. Проблема в том, что для этого не помешало бы быть root&#39;ом, но не смотря на запрос пароля суперпользователя, spice-client-glib-usb-acl-helper выполняется с правами обычного пользователя из-под которого запущен spicy. Короче, где-то недокостылили. Самым простым решением этой проблемы является установка бита &lt;b&gt;setuid&lt;/b&gt; на файл spice-client-glib-usb-acl-helper:&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;chmod u+s /usr/bin/spice-client-glib-usb-acl-helper&lt;/span&gt;&lt;/blockquote&gt;
После этого действия USB-устройства начинают успешно пробрасываться.&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Для удобства теперь можно также отключить запрос пароля суперпользователя. Делается это через &lt;a href=&quot;https://ru.wikipedia.org/wiki/Polkit&quot;&gt;polkit&lt;/a&gt;. В файле &lt;b&gt;/usr/share/polkit-1/actions/org.spice-space.lowlevelusbaccess.policy&lt;/b&gt; надо отредактировать содержимое элемента &lt;b&gt;defaults&lt;/b&gt; следующим образом:&lt;/div&gt;
&lt;script class=&quot;brush:xml&quot; type=&quot;syntaxhighlighter&quot;&gt;&lt;![CDATA[
&lt;defaults&gt;
    &lt;allow_inactive&gt;no&lt;/allow_inactive&gt;
    &lt;allow_active&gt;yes&lt;/allow_active&gt;
&lt;/defaults&gt;
]]&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/4937414447932051194/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2016/02/usb-redirection-error-spicy-opensuse.html#comment-form' title='Комментарии: 3'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/4937414447932051194'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/4937414447932051194'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2016/02/usb-redirection-error-spicy-opensuse.html' title='USB redirection error в Spicy на openSUSE'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-90772426571912234</id><published>2015-08-22T16:08:00.000+03:00</published><updated>2015-08-22T16:08:48.303+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="красиво"/><title type='text'>Skype 4.3 без PulseAudio</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Как известно, Microsoft безжалостно выпилила поддержку ALSA из Skype 4.3. А я, в свою очередь, недавно пару вечеров старательно выпиливал этот PulseAudio из openSUSE 13.2, после того, как никакими молотками и &quot;волшебными&quot; русскими словами не смог заставить этот самый PulseAudio видеть порт line-in на старой карте видео-захвата. Кроме того, я попытался снять звук обычной звуковой картой и получил на записи великолепный белый шум, хотя ожидал услышать немного другое.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
После &quot;лечения&quot; системы от PulseAudio осталась только одна проблема - неработающий Skype. В интернетах сразу нашел &lt;a href=&quot;http://habrahabr.ru/post/241045/&quot;&gt;совет использовать эмулятор PulseAudio для ALSA&lt;/a&gt; - &lt;a href=&quot;https://github.com/i-rinat/apulse&quot;&gt;apulse&lt;/a&gt;. Нормальный такой рабочий костыль. Один из вариантов настройки apulse заключается в создании файла &lt;b&gt;~/.asoundrc&lt;/b&gt;. У меня он, например, такого содержания:&lt;/div&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:text&quot;&gt;pcm.!default {
  type asym
    playback.pcm {
      type plug
      slave.pcm &quot;hw:1,0&quot;
    }
    capture.pcm {
      type plug
      slave.pcm &quot;hw:3,0&quot;
    }
}
&lt;/pre&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Где &quot;&lt;b&gt;hw:1,0&lt;/b&gt;&quot; и &quot;&lt;b&gt;hw:3,0&lt;/b&gt;&quot; это ALSA-идентификаторы устройств воспроизведения и захвата звука, соответственно. Но такой подход оказался не очень удобным, потому что в компьютере у меня одновременно было 2 устройства воспроизведения и 3 устройства захвата звука. И если ID основного устройства для вывода звука оказывался всегда &quot;hw:1,0&quot;, то нужное устройство захвата периодически оказывалось то с ID &quot;hw:3,0&quot;, то &quot;hw:2,0&quot;. Ну и, понятное дело, чтобы Skype начал получать звук с правильного устройства, надо было поменять его ID в файле .asoundrc и после этого перезапустить Skype через apulse:&lt;/div&gt;
&lt;br /&gt;
&lt;pre&gt;apulse skype
&lt;/pre&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Наверное, можно прибить гвоздями все ID к соответствующим звуковым устройствам через UDEV, но как-то мне всегда лень &quot;лазить через Google&quot; в его конфиги. Поэтому предлагаю более простой и универсальный вариант динамической настройки apulse.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Он основывается на том, что указать устройства для apulse можно с помощью установленных переменных окружения &lt;b&gt;APULSE_PLAYBACK_DEVICE&lt;/b&gt; и &lt;b&gt;APULSE_CAPTURE_DEVICE&lt;/b&gt;. А с помощью программ aplay и arecord можно легко выдернуть циферки, которые присвоены вашим звуковым устройствам.&lt;/div&gt;
&lt;br /&gt;
Например, вот мой вывод команды aplay -l:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;**** List of PLAYBACK Hardware Devices ****
card 0: HDMI [HDA Intel HDMI], device 3: HDMI 0 [HDMI 0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: HDMI [HDA Intel HDMI], device 7: HDMI 1 [HDMI 1]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: HDMI [HDA Intel HDMI], device 8: HDMI 2 [HDMI 2]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: PCH [HDA Intel PCH], device 0: ALC1150 Analog [ALC1150 Analog]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: PCH [HDA Intel PCH], device 1: ALC1150 Digital [ALC1150 Digital]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
&lt;/pre&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Интересующее меня устройство вывода называется ALC1150 Analog. Команда play -l говорит что ему присвоены идентификаторы card 1 и device 0. Из этих сочетаний в моем случае надо сформировать имя &quot;hw:1,0&quot;. В других дистрибутивах имя может строиться немного по-другому, например, &quot;plughw:1,0&quot;.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Таким образом, всё что надо сделать это выдернуть циферки, назначенные устройствам воспроизведения и захвата, сформировать для них валидные ALSA-идентификаторы и записать их в соответствующие переменные окружения. Автоматизировать эту процедуру можно добавлением следующих строчек в ваш файл ~/.profile, который будет выполнятся при каждом входе в систему под вашим именем:&lt;/div&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;PLAY_DEV_NAME=&quot;ALC1150 Analog&quot;
REC_DEV_NAME=&quot;U0x46d0x804&quot;

PLAY_CARD=`aplay -l |grep &quot;$PLAY_DEV_NAME&quot; | grep -E -o &quot;card +[0-9]+&quot;`
PLAY_DEV=`aplay -l |grep &quot;$PLAY_DEV_NAME&quot; | grep -E -o &quot;device +[0-9]+&quot;`
APULSE_PLAYBACK_DEVICE=&quot;hw:${PLAY_CARD##card },${PLAY_DEV##device }&quot;

REC_CARD=`arecord -l |grep &quot;$REC_DEV_NAME&quot; | grep -E -o &quot;card +[0-9]+&quot;`
REC_DEV=`arecord -l |grep &quot;$REC_DEV_NAME&quot; | grep -E -o &quot;device +[0-9]+&quot;`
APULSE_CAPTURE_DEVICE=&quot;hw:${REC_CARD##card },${REC_DEV##device }&quot;

export APULSE_PLAYBACK_DEVICE APULSE_CAPTURE_DEVICE
&lt;/pre&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Только нужно поменять значения переменных &lt;b&gt;PLAY_DEV_NAME&lt;/b&gt; и &lt;b&gt;REC_DEV_NAME&lt;/b&gt; на имена ваших устройств.&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/90772426571912234/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2015/08/skype-43-pulseaudio.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/90772426571912234'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/90772426571912234'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2015/08/skype-43-pulseaudio.html' title='Skype 4.3 без PulseAudio'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-4550492316894312699</id><published>2015-06-29T05:03:00.000+03:00</published><updated>2015-12-08T01:29:45.281+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Eclipse"/><category scheme="http://www.blogger.com/atom/ns#" term="GTK"/><category scheme="http://www.blogger.com/atom/ns#" term="Программирование"/><title type='text'>Eclipse Mars, GTK+ 3, Adwaita и маленький CSS-напильник</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
С переходом на новый Eclipse Mars, я решил кое-что ещё поменять в своей жизни. Хотя правильнее сказать, что сам Eclipse предложил мне эти изменения, а я не стал сопротивляться. Как известно, Eclipse Mars теперь по умолчанию использует GTK+ версии 3. Для второй версии GTK+ у меня уже много лет стоит тема  oxygen-gtk. Страшненькая, да... но уже привычная. Поэтому как-то даже мысли не возникало попробовать поменять её. А тут оказалось, что для GTK+ 3 у меня вообще эта тема не стоит, и используется тема по умолчанию, которая называется Adwaita. И что-то я так поглядел на неё, и в принципе мой глаз посчитал её чуть менее страшной и чуть более секси, чем  oxygen-gtk. Осталось только решить несколько бросившихся сразу в глаза проблем.&lt;/div&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Проблемы&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Проблема 1.&lt;/b&gt; Какое-то неадекватно большое значение padding у большинства GUI-элементов.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglmaXcZajzR5Q51cn6XLhCFgwzIUyAlv81s8a87bimwpz_hZb-XJSARr_T7zcOTlYbCrOA4-MVYPk_VTGj8sMEkO0S3D66wgo3z6y64Y5FuH8E_yhtgu7zkO4HHCNxf7ONxuangqeCdStc/s1600/eclipse-adwaita-1.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;80&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglmaXcZajzR5Q51cn6XLhCFgwzIUyAlv81s8a87bimwpz_hZb-XJSARr_T7zcOTlYbCrOA4-MVYPk_VTGj8sMEkO0S3D66wgo3z6y64Y5FuH8E_yhtgu7zkO4HHCNxf7ONxuangqeCdStc/s640/eclipse-adwaita-1.jpg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;Проблема 2.&lt;/b&gt; Странный tooltip для javadoc с белым шрифтом на чёрном фоне.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmPdLSKuqGaXud0Be24fdJ5XpGFw410e0MY61wkEkC-_HVTklC0Hc1XKhRzG8SvWjxXNnZdh8vRmT4Q3F4TI22AW3R0qfpFvFfWYz6iOHBElBScLZenjk31wOotJMAhRCat66xR_BdDKKn/s1600/eclipse-adwaita-2.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;190&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmPdLSKuqGaXud0Be24fdJ5XpGFw410e0MY61wkEkC-_HVTklC0Hc1XKhRzG8SvWjxXNnZdh8vRmT4Q3F4TI22AW3R0qfpFvFfWYz6iOHBElBScLZenjk31wOotJMAhRCat66xR_BdDKKn/s640/eclipse-adwaita-2.jpg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&amp;nbsp; &lt;br /&gt;
&lt;b&gt;Проблема 3. &lt;/b&gt;Смазанные шрифты после скролинга некоторых окон (Project Explorer, Package Explorer, Outline). Особенно ярко проблема проявляется, если скролить колесом мыши.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwZHlkqrvAgPvuXHIylPFoMdzPe-hB_kLo3-1btLypLSRkMNhky_dxgWKQ590UXp-n7clnuv_MyuMRGFaMH82qKDaXgA_R_mgAM4HAuCkA0RRkQTjXrHSkaPrN0BwtKzB11N7Qt9D8sF75/s1600/eclipse-adwaita-3.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwZHlkqrvAgPvuXHIylPFoMdzPe-hB_kLo3-1btLypLSRkMNhky_dxgWKQ590UXp-n7clnuv_MyuMRGFaMH82qKDaXgA_R_mgAM4HAuCkA0RRkQTjXrHSkaPrN0BwtKzB11N7Qt9D8sF75/s1600/eclipse-adwaita-3.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Решение&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
В GTK+ 3 для описания тем больше не используются rc-файлы собственного формата. Теперь для стилизации применяются более понятные CSS-подобные файлы. &lt;br /&gt;
&lt;br /&gt;
Проблемы 1 и 2 решаются созданием файла &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;~/.config/gtk-3.0/gtk.css&lt;/span&gt; со следующим содержанием:&lt;br /&gt;
&lt;pre class=&quot;brush:css&quot;&gt;* {
    padding: 1px;
}

GtkToolbar {
    padding: 2px;
}

GtkMenuBar {
    padding: 2px;
}

GtkMenuItem {
    padding: 2px 6px;
}

.tooltip {
    background-color: #CAE1FF;
    color: #333;
    text-shadow: none;
}
&lt;/pre&gt;
&lt;br /&gt;
Кратко поясню. Сначала устанавливаем &lt;b&gt;padding&lt;/b&gt; абсолютно для всех элементов равным 1px. Затем для элементов &lt;b&gt;GtkToolbar&lt;/b&gt;, &lt;b&gt;GtkMenuBar&lt;/b&gt; и &lt;b&gt;GtkMenuItem&lt;/b&gt; делаем отступы чуть более разумными. CSS-класс &lt;b&gt;tooltip&lt;/b&gt; вероятно прописан где-то в коде и назначен, как для всплывающего окна javadoc, так и для хинтов различных графических элементов. Свойство &lt;b&gt;text-shadow&lt;/b&gt; необходимо, чтобы ликвидировать тень текста хинта, которая становится заметной и неуместной после изменения его фона и цвета текста:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYRXlqzAyvCL2IRuJa6pQ1htb1bIM_OiKlkvrS9gR8VtuhZojd_k29jGAhOr9sbFJfAb-Hrg6_4hAt68v6fnnbwvtA-wQz4Rl5XyMEMy1OIa5W371ru0tBSh9uEcubSxP1szfeD7WDfm4-/s1600/eclipse-adwaita-4.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYRXlqzAyvCL2IRuJa6pQ1htb1bIM_OiKlkvrS9gR8VtuhZojd_k29jGAhOr9sbFJfAb-Hrg6_4hAt68v6fnnbwvtA-wQz4Rl5XyMEMy1OIa5W371ru0tBSh9uEcubSxP1szfeD7WDfm4-/s1600/eclipse-adwaita-4.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Обратите внимание, что файл &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;~/.config/gtk-3.0/gtk.css&lt;/span&gt; будет действовать и переопределять стили для любой темы GTK+ 3, выбранной пользователем, у которого в домашнем каталоге есть этот файл, а не только для темы Adwaita.&lt;br /&gt;
&lt;br /&gt;
Проблему 3 я решил довольно тупо, сняв флажок с &lt;b&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Use mixed fonts and colors for labels&lt;/span&gt;&lt;/b&gt; в настройках внешнего вида (Window /  Preferences / General / Appearance). Мне легче смириться с одноцветным черным лейблом, чем с таким поломанным шрифтом.&lt;br /&gt;
&lt;br /&gt;
Тут я вынужден обновить сообщение и добавить, что после решения проблемы 3 появилась &lt;b&gt;проблема 4&lt;/b&gt; :) которая выглядет, как практически нечитаемый выбранный пункт во всплывающем окне автодополнения.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdDnocekzLk_hDQU0xRt9Wz9BL9SDml5WyjTl6qFHQdlPY7WAJwvEoziNtGwDznX8kgz3oTvQ2WelsYGL3RYkfq5bmvy4Rf_8LsAcEDTPE9Qh1_dqz2oHzQIBCicD_oPDn8w6HLKTvjSlr/s1600/eclipse-adwaita-9.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdDnocekzLk_hDQU0xRt9Wz9BL9SDml5WyjTl6qFHQdlPY7WAJwvEoziNtGwDznX8kgz3oTvQ2WelsYGL3RYkfq5bmvy4Rf_8LsAcEDTPE9Qh1_dqz2oHzQIBCicD_oPDn8w6HLKTvjSlr/s1600/eclipse-adwaita-9.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Эта неприятность также фиксится добавлением некоторых строчек в gtk.css:&lt;br /&gt;
&lt;pre class=&quot;brush:css&quot;&gt;GtkTreeView:selected {
    color: #FFF;
    background-color: #4A90D9;
}
&lt;/pre&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Результаты&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhK2WB-brvqX9DViSsaX2McsAItQal2BKUmy15slonmicCBiEUjF9vfF5BN0ORKaQklPvJXeDxBdS246iO7gy1MNe125tO6l5jtwBZNQT6AZA8kyoVGUsfOfPRG5FkBeqWcNulsJeKnbDMP/s1600/eclipse-adwaita-5.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;62&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhK2WB-brvqX9DViSsaX2McsAItQal2BKUmy15slonmicCBiEUjF9vfF5BN0ORKaQklPvJXeDxBdS246iO7gy1MNe125tO6l5jtwBZNQT6AZA8kyoVGUsfOfPRG5FkBeqWcNulsJeKnbDMP/s640/eclipse-adwaita-5.jpg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhw0GJO6uSxI86I06JLv3bxxNJay8kXBeBdRdfNC1knjUNGfznvz2uDZiMQBxWTxwxy1twePZ0hkErJBEycMMmnLbaKtW6NR8IqdgxSVocOKRWbUD11fh3ZlJ3Fbjoxa9lDdQCWZiDwd_sm/s1600/eclipse-adwaita-6.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;188&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhw0GJO6uSxI86I06JLv3bxxNJay8kXBeBdRdfNC1knjUNGfznvz2uDZiMQBxWTxwxy1twePZ0hkErJBEycMMmnLbaKtW6NR8IqdgxSVocOKRWbUD11fh3ZlJ3Fbjoxa9lDdQCWZiDwd_sm/s640/eclipse-adwaita-6.jpg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBag14NR1k_8nSKBxC7ZKAKg5pP5lrC6Lov3yeJ9wwqsTPzkAIannM2JmXR0jdCxMFA0LAYTSrn14nBwjVHG8wHfR1g9QQoFO84HnATUacaWhhKG_FP-JyXeof2C_XZ6rfcqfDvu88p-XE/s1600/eclipse-adwaita-7.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBag14NR1k_8nSKBxC7ZKAKg5pP5lrC6Lov3yeJ9wwqsTPzkAIannM2JmXR0jdCxMFA0LAYTSrn14nBwjVHG8wHfR1g9QQoFO84HnATUacaWhhKG_FP-JyXeof2C_XZ6rfcqfDvu88p-XE/s1600/eclipse-adwaita-7.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbrzj7PBEN4ZYwL3nOl9gGNHZ_GY-loqDegnIe3AVuU9MWm8buLtu-r4a5oZ_kceeVPW7lifYOUJV4KH21CulyT9xORtHb0Iv3tAWFcfeS3aYd-Zs6mMc0MmiI2KD24gy7novgGXhMPoEv/s1600/eclipse-adwaita-8.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbrzj7PBEN4ZYwL3nOl9gGNHZ_GY-loqDegnIe3AVuU9MWm8buLtu-r4a5oZ_kceeVPW7lifYOUJV4KH21CulyT9xORtHb0Iv3tAWFcfeS3aYd-Zs6mMc0MmiI2KD24gy7novgGXhMPoEv/s1600/eclipse-adwaita-8.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpRDqejXqqB_f54j1_Ola8nGey28Rkk6jMTvHjpRHG4y_HFa03nQ61nxNhwsz6DoPwY0dp3e7JveVAoluXdbB2MNTy1q3DkEY55_6mvWDDxcIz8_bpaQU79j1cJlS0z5lQIEQMGJKbGZJi/s1600/eclipse-adwaita-10.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpRDqejXqqB_f54j1_Ola8nGey28Rkk6jMTvHjpRHG4y_HFa03nQ61nxNhwsz6DoPwY0dp3e7JveVAoluXdbB2MNTy1q3DkEY55_6mvWDDxcIz8_bpaQU79j1cJlS0z5lQIEQMGJKbGZJi/s1600/eclipse-adwaita-10.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Ссылки&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Тем, кто захочет ещё что-нибудь подкрутить или сделать более тонкую настройку, а не такую кувалдообразную, как у меня, рекомендую сначала заглянуть &lt;a href=&quot;https://developer.gnome.org/gtk3/stable/&quot;&gt;сюда&lt;/a&gt;, и конкретно &lt;a href=&quot;https://developer.gnome.org/gtk3/stable/gtk-migrating-GtkStyleContext.html&quot;&gt;вот сюда&lt;/a&gt;. А ещё &lt;a href=&quot;https://wiki.archlinux.org/index.php/GTK%2B&quot;&gt;здесь&lt;/a&gt; интересно.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Когда терпение кончилось&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;Спустя некоторое время, должен признать, что это далеко не все глюки с UI, которые мне повстречались при использовании GTK+ 3 в &lt;span style=&quot;font-family: inherit;&quot;&gt;E&lt;/span&gt;clipse. &lt;span style=&quot;font-family: inherit;&quot;&gt;И &lt;span style=&quot;font-family: inherit;&quot;&gt;у меня&lt;span style=&quot;font-family: inherit;&quot;&gt; &lt;/span&gt;больше&lt;/span&gt; нет сил подкручивать эти гайки. Я&lt;/span&gt; устал, я ухожу. Теперь &lt;span style=&quot;font-family: inherit;&quot;&gt;перед запуском Eclipse у меня выполняется export SWT_GTK3=0, что&lt;span style=&quot;font-family: inherit;&quot;&gt; заставляет Eclipse использовать GTK+ 2 по старинке.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/4550492316894312699/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2015/06/eclipse-mars-gtk-3-adwaita-css.html#comment-form' title='Комментарии: 4'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/4550492316894312699'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/4550492316894312699'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2015/06/eclipse-mars-gtk-3-adwaita-css.html' title='Eclipse Mars, GTK+ 3, Adwaita и маленький CSS-напильник'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglmaXcZajzR5Q51cn6XLhCFgwzIUyAlv81s8a87bimwpz_hZb-XJSARr_T7zcOTlYbCrOA4-MVYPk_VTGj8sMEkO0S3D66wgo3z6y64Y5FuH8E_yhtgu7zkO4HHCNxf7ONxuangqeCdStc/s72-c/eclipse-adwaita-1.jpg" height="72" width="72"/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-4852011171344075195</id><published>2015-06-10T06:59:00.002+03:00</published><updated>2015-06-10T07:27:52.444+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Redmine"/><category scheme="http://www.blogger.com/atom/ns#" term="Базы данных"/><title type='text'>Перенос существующей базы данных Redmine с SQLite на PostgreSQL</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Разглагольствовать не буду. Скажу лишь, что попробовал несколько различных способов, кроме ручного исправления дампа SQLite, ибо лень. Самым простым и жизнеспособным оказался метод с использованием программы &lt;b&gt;&lt;a href=&quot;https://github.com/ricardochimal/taps&quot;&gt;Taps&lt;/a&gt;&lt;/b&gt;. Эта чудо-ruby-штука поднимает http-сервер с подключением к базе данных &lt;b&gt;А&lt;/b&gt; и позволяет с помощью клиента выполнять обмен данными между этой базой &lt;b&gt;А&lt;/b&gt; и какой-либо базой &lt;b&gt;Б&lt;/b&gt;, даже если они управляются различными СУБД. Конечно, её возможности не безграничны, но для миграции базы данных Redmine их хватило.&lt;br /&gt;
&lt;br /&gt;
Все ниже описанные действия выполнялись на openSUSE 13.2.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Кратко и по сути&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
1. Установить через &lt;a href=&quot;http://habrahabr.ru/post/120188/&quot;&gt;RubyGems&lt;/a&gt; программу Taps:&lt;br /&gt;
&lt;pre class=&quot;brush: text; gutter: false;&quot;&gt;gem install taps&lt;/pre&gt;
2. Подготовить базу данных PostgreSQL для Redmine:&lt;br /&gt;
&lt;pre class=&quot;brush: text; gutter: false;&quot;&gt;createuser -U postgres redmine
psql -U postgres
postgres=# ALTER USER redmine WITH PASSWORD &#39;redmine_password&#39;;
postgres=#\q
createdb -U postgres -O redmine redmine&lt;/pre&gt;
3. Запустить сервер Taps с подключением к новой созданной в PostgreSQL базе для Redmine:&lt;br /&gt;
&lt;pre class=&quot;brush: text; gutter: false;&quot;&gt;taps.ruby2.1 server postgres://redmine:redmine_password@localhost/redmine taps taps_password&lt;/pre&gt;
4. Используя клиент Taps, запушить все данные из исходной базы SQLite в PostgreSQL:&lt;br /&gt;
&lt;pre class=&quot;brush: text; gutter: false;&quot;&gt;taps.ruby2.1 push sqlite:///usr/local/redmine/db/redmine.db.sqlite3 http://taps:taps_password@localhost:5000&lt;/pre&gt;
5. Не забыть изменить настройки подключения к БД в Redmine (redmine/config/database.yml):&lt;br /&gt;
&lt;pre class=&quot;brush: text; gutter: false;&quot;&gt;production:
  adapter: postgresql
  database: redmine
  host: localhost
  username: redmine
  password: &quot;redmine_password&quot;
&lt;/pre&gt;
5. Перезапустить Redmine&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Грабли&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
При запуске сервер валится приблизительно с такой портянкой:&lt;br /&gt;
&lt;pre class=&quot;brush: text; gutter: false;&quot;&gt;/usr/lib/ruby/gems/2.1.0/gems/sinatra-1.0/lib/sinatra/base.rb:298:in `&lt;module:templates&gt;&#39;: uninitialized constant Tilt::CompileSite (NameError)
        from /usr/lib/ruby/gems/2.1.0/gems/sinatra-1.0/lib/sinatra/base.rb:297:in `&lt;module:sinatra&gt;&#39;
        from /usr/lib/ruby/gems/2.1.0/gems/sinatra-1.0/lib/sinatra/base.rb:21:in `&lt;top required=&quot;&quot;&gt;&#39;
        from /usr/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:73:in `require&#39;
        from /usr/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:73:in `require&#39;
        from /usr/lib/ruby/gems/2.1.0/gems/taps-0.3.24/lib/taps/server.rb:1:in `&lt;top required=&quot;&quot;&gt;&#39;
        from /usr/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:73:in `require&#39;
        from /usr/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:73:in `require&#39;
        from /usr/lib/ruby/gems/2.1.0/gems/taps-0.3.24/lib/taps/cli.rb:61:in `server&#39;
        from /usr/lib/ruby/gems/2.1.0/gems/taps-0.3.24/lib/taps/cli.rb:27:in `run&#39;
        from /usr/lib/ruby/gems/2.1.0/gems/taps-0.3.24/bin/taps:6:in `&lt;top required=&quot;&quot;&gt;&#39;
        from /usr/bin/taps.ruby2.1:23:in `load&#39;
        from /usr/bin/taps.ruby2.1:23:in `&lt;main&gt;&#39;
&lt;/main&gt;&lt;/top&gt;&lt;/top&gt;&lt;/top&gt;&lt;/module:sinatra&gt;&lt;/module:templates&gt;&lt;/pre&gt;
В этом случае необходимо проверить какие версии библиотеки Tilt у вас установлены:&lt;br /&gt;
&lt;pre class=&quot;brush: text; gutter: false;&quot;&gt;zypper se -s tilt&lt;/pre&gt;
Если среди установленных есть Tilt версии 2, то необходимо её снести и поставить 1.4. Как правило, zypper сам ставит версию 1.4 вместо удалённой 2.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Ещё&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Уже после публикации этой статьи, я наткнулся ещё на одну чудо-ruby-штуку под названием &lt;b&gt;&lt;a href=&quot;https://github.com/yamldb/yaml_db&quot;&gt;YamlDb&lt;/a&gt;&lt;/b&gt;. Жалко, что она сразу мне не попалась, меньше бы времени потратил. И на Хабре давно уже есть &lt;a href=&quot;http://habrahabr.ru/post/117797/&quot;&gt;статейка&lt;/a&gt;.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/4852011171344075195/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2015/06/redmine-sqlite-postgresql.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/4852011171344075195'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/4852011171344075195'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2015/06/redmine-sqlite-postgresql.html' title='Перенос существующей базы данных Redmine с SQLite на PostgreSQL'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-379817752111327346</id><published>2014-10-11T14:23:00.000+04:00</published><updated>2016-06-27T03:49:28.198+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="network"/><category scheme="http://www.blogger.com/atom/ns#" term="администрирование"/><category scheme="http://www.blogger.com/atom/ns#" term="красиво"/><title type='text'>Автоматический запуск скриптов при подключении NetworkManager</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Я очень долго отказывался от использования NetworkManager (далее NM) и жил с традиционным и понятным ifup. Но теперь, пожалуй, пришло время приобщиться к цивилизации. Тем более, что NM стал уже не такой сурово глючный, как раньше, да и что там говорить, удобно на ноутбуке перещёлкиваться между проводом, WiFi и GSM-модемом.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Единственной преградой на пути к благам цивилизации для меня было то, что мне приходится использовать программу &lt;b&gt;&lt;a href=&quot;http://vtun.sourceforge.net/&quot;&gt;VTun&lt;/a&gt;&lt;/b&gt; для создания туннеля между домом и работой, через который я также хожу в глобальную сеть, а NM не поддерживает VTun из коробки. Конечно, всегда можно наваять пару скриптов, которые будут периодически смотреть поднятые интерфейсы, пинговать различные ip-адреса и заниматься прочими делами, чтобы понять в каком состоянии находится сеть. Но, как оказалось, можно решить эту задачу и более красивым способом с помощью &lt;b&gt;NetworkManager Dispatcher Service&lt;/b&gt;. В &lt;b&gt;systemd&lt;/b&gt; он обычно имеет имя &lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;b&gt;NetworkManager-dispatcher.service&lt;/b&gt;.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
NetworkManager Dispatcher Service это сервис, который при изменении состояния NM, дёргает скрипты из каталога &lt;b&gt;/etc/NetworkManager/dispatcher.d&lt;/b&gt; и передает в них в качестве параметров командной строки имя сетевого интерфейса, у которого изменилось состояние, и новое значение этого самого состояния - up или down (vpn-up и vpn-down для VPN-соединений).&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;/div&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Типичный шаблон скрипта в каталоге /etc/NetworkManager/dispatcher.d может выглядеть, например, так:&lt;/div&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;#!/bin/bash

IF=$1      # имя сетевого интерфейса, с которым связано событие
STATUS=$2  # новое состояние сетевого интерфейса

if [ $IF = &quot;eth0&quot; ]; then
  case $STATUS in
    up)
      # команды выполняемые после установления соединения
      ;;
    down)
      # команды выполняемые после разрыва соединения
      ;;
  esac
fi
&lt;/pre&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Для того, чтобы Dispatcher Service смог запустить ваш скрипт, ему должен быть назначен владелец root и у него должно быть право на выполнение этого скрипта.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Дома я хожу в интернет через роутер, который умеет раздавать этот  самый интернет по проводам и по WiFi. Мне хотелось, чтобы у меня в NM  было два соединения. Первое соединение обычное, которое выполняет  подключение к роутеру и получает от него IP по DHCP, после чего у меня  появляется доступ в интернет через провайдера. Второе соединение делает  изначально всё тоже самое что и первое, но после подключения к роутеру  осуществляет ещё запуск программы VTun для установки туннеля и добавляет  несколько статических маршрутов, которые должны направлять пакеты на  локальные адреса моего провайдера через реальный интерфейс, а не через  туннель. Туннель используется как для доступа к машинам на работе, так и  для хождения в интернет через рабочий канал.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Т. к. я хочу иметь два соединения в NM, повешанных на один сетевой интерфейс, и при запуске только одного из этих соединений должен также подниматься туннель, то скрипту необходимо знать имя активного соединения. Активные соединения можно посмотреть командой &lt;b&gt;nmcli c show --active&lt;/b&gt;. Эта команда выводит список всех активных соединений в виде таблицы. Но мне нужно проверить активно ли одно конкретное соединение с указанным именем. Это можно сделать, передав выше указанной команде идентификатор интересующего меня соединения: &lt;b&gt;nmcli c show --active id ID_СОЕДИНЕНИЯ&lt;/b&gt;. После выполнения этой команды для активного соединения будет выведено множество строк с различными параметрами этого соединения, а если выполнить эту команду для неактивного соединения, то вывод будет пустой. Я, честно говоря, не понял чем отличаются и отличаются ли вообще понятия ID соединения и NAME соединения в NetworkManager, но по моим наблюдениям они всегда совпадают.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Основываясь на вышесказанном, мой скрипт получился таким:&lt;/div&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;#!/bin/bash

IF=$1
STATUS=$2

VTUND_SCRIPT=/usr/local/etc/vtun/vtund.sh

REQUIRED_IF=&quot;eth0&quot;
REQUIRED_CON=&quot;vtun&quot;

if [ $IF = $REQUIRED_IF ]; then
  case $STATUS in
    up)
      # посчитать количество строк - если соединение неактивно, то их кол-во = 0
      (( `nmcli c show --active id $REQUIRED_CON | wc -l`  &amp;gt; 0 )) &amp;amp;&amp;amp; VTUN_CONNECTION=true || VTUN_CONNECTION=false

      if [ $VTUN_CONNECTION = true ]; then
        $VTUND_SCRIPT &amp;amp;
      fi
      ;;
    down)
        killall vtund.sh
        killall vtund
      ;;
  esac
fi
&lt;/pre&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
В скрипте, при поднятии соединения на интерфейсе eth0, сначала проверяем не является ли активным соединение с именем vtun, которое я назначил в настройках NM соединению, отвечающему за поднятие туннеля, и привязал его явно к интерфейсу eth0:&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYNv9UYyF2Xdby1TUmWcHUP9zOBFPjUDVR4f2tSBDma9heJseXN80Y-rIgILRvAaucXwW-VOhWlJKIW2RLQrpopVI1YwqRaCZ7jxii4MMRArReY70bjMgX9joywjEVO0ErakSsQogz7d9T/s1600/vtun_screenshot.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYNv9UYyF2Xdby1TUmWcHUP9zOBFPjUDVR4f2tSBDma9heJseXN80Y-rIgILRvAaucXwW-VOhWlJKIW2RLQrpopVI1YwqRaCZ7jxii4MMRArReY70bjMgX9joywjEVO0ErakSsQogz7d9T/s400/vtun_screenshot.png&quot; width=&quot;348&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Если на интерфейсе eth0 поднялось соединение с именем vtun, значит можно запускать скрипт для установки туннеля: &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&quot;$VTUND_SCRIPT&quot; &amp;amp;&lt;/span&gt;&lt;/b&gt;. Обратите внимание, что для вызова скрипта, запускающего программу vtun, я использую амперсанд, чтобы этот скрипт выполнялся в фоновом режиме. Это очень важный момент, потому что у Dispatcher Service есть таймаут для выполнения  каждого скрипта из каталога &lt;b&gt;/etc/NetworkManager/dispatcher.d&lt;/b&gt; и если какой-либо скрипт не успевает выполниться за  время таймаута, Dispatcher Service просто грохает его. Ещё я заметил, что в некоторых дистрибутивах скрипты Dispatcher Service запускаются с очень бедным окружением, поэтому возможно придется прописывать полные пути до каких-либо программ, используемых в этих скриптах. Но в принципе, это нормально и вообще является правилом хорошего тона для подобных автоматически запускаемых скриптов. При опускании интерфейса eth0 убиваются скрипт vtund.sh и сам демон vtund.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Вот в принципе и всё о чем хотелось написать. Ещё немного почитать о Dispatcher Service можно &lt;a href=&quot;https://wiki.archlinux.org/index.php/NetworkManager#Network_services_with_NetworkManager_dispatcher&quot;&gt;здесь&lt;/a&gt;. Там есть пара примеров и более подробная информация о таймауте.&lt;/div&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/379817752111327346/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2014/10/networkmanager.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/379817752111327346'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/379817752111327346'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2014/10/networkmanager.html' title='Автоматический запуск скриптов при подключении NetworkManager'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYNv9UYyF2Xdby1TUmWcHUP9zOBFPjUDVR4f2tSBDma9heJseXN80Y-rIgILRvAaucXwW-VOhWlJKIW2RLQrpopVI1YwqRaCZ7jxii4MMRArReY70bjMgX9joywjEVO0ErakSsQogz7d9T/s72-c/vtun_screenshot.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-4849985456109889323</id><published>2014-04-04T13:43:00.000+04:00</published><updated>2014-04-04T13:46:13.661+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BUGS"/><category scheme="http://www.blogger.com/atom/ns#" term="KDE"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="openSUSE"/><category scheme="http://www.blogger.com/atom/ns#" term="гадость"/><title type='text'>Баг с shortcut&#39;ами в qt4</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;Вдруг странным образом перестали работать сочетания клавиш в qt-программах. Например Ctrl+C, Ctrl+V, Ctrl+X в Dolphin или Ctrl+Alt+T в Yakuake. Скорее всего, к этому списку относятся все сочетания с участием Ctrl и Alt. Оказалось, что ковыряясь в настройках раскладки KDE после сноса бесполезного ibus, я переместил русский язык на первое место, тем самым сделав его раскладкой по умолчанию. Возвращение US-раскладки в зад, т.е. на первую строчку, всё чинит. Такая вот &lt;a href=&quot;https://bugs.kde.org/show_bug.cgi?id=309193&quot;&gt;багулька&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk9KROTlmNcZ8IWbXR6zZ7gZIKxwrS030SzRiIsG0wjaREcv2yIsqnmUPl9jRMMTvuQGhIzgKx_MUchUIhyphenhyphenE9vaMySNQHs6gg-YKXu0f9cBEenLK1Ud_1APj6ONcVF8o2Wk5Ou65U5bcti/s1600/kb_layout.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk9KROTlmNcZ8IWbXR6zZ7gZIKxwrS030SzRiIsG0wjaREcv2yIsqnmUPl9jRMMTvuQGhIzgKx_MUchUIhyphenhyphenE9vaMySNQHs6gg-YKXu0f9cBEenLK1Ud_1APj6ONcVF8o2Wk5Ou65U5bcti/s1600/kb_layout.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/4849985456109889323/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2014/04/shortcut-qt4.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/4849985456109889323'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/4849985456109889323'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2014/04/shortcut-qt4.html' title='Баг с shortcut&#39;ами в qt4'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk9KROTlmNcZ8IWbXR6zZ7gZIKxwrS030SzRiIsG0wjaREcv2yIsqnmUPl9jRMMTvuQGhIzgKx_MUchUIhyphenhyphenE9vaMySNQHs6gg-YKXu0f9cBEenLK1Ud_1APj6ONcVF8o2Wk5Ou65U5bcti/s72-c/kb_layout.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-4310162396556111193</id><published>2013-10-29T14:01:00.001+04:00</published><updated>2014-01-14T02:21:34.043+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Google"/><category scheme="http://www.blogger.com/atom/ns#" term="GWT"/><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><category scheme="http://www.blogger.com/atom/ns#" term="Программирование"/><title type='text'>Выбор года и месяца в GWT DatePicker с помощью ListBox</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Стандартный &lt;b&gt;DatePicker&lt;/b&gt; в &lt;b&gt;GWT&lt;/b&gt; очень неудобный. Для выбора нужного года и месяца пользователь вынужден кликать по соответствующим стрелочкам и перемещаться на один месяц вперед или на зад. Например, если пользователю необходимо выбрать свою дату рождения, скажем, 29 февраля 1980 года, а в календаре по умолчанию установлена текущая дата 25 октября 2013 года, то представьте сколько раз нужно будет кликнуть бедняге по стрелочке, чтобы добраться до дня своего рождения!&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0GAa9KI7HsQj4EKu-AVxtXJ9KIm5TubqYK2fuZjSPG_nqr2-LBwPdjH36JSO6bxuj9WZqYidzXoxM1Jwq1AzobkaninICZjccp-CYKL77fxpte9FbUi4sfrOZIRn5wcOd53xJRIYvgyr4/s1600/std-date-picker.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0GAa9KI7HsQj4EKu-AVxtXJ9KIm5TubqYK2fuZjSPG_nqr2-LBwPdjH36JSO6bxuj9WZqYidzXoxM1Jwq1AzobkaninICZjccp-CYKL77fxpte9FbUi4sfrOZIRn5wcOd53xJRIYvgyr4/s1600/std-date-picker.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Стандартный DatePicker в GWT&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Конечно, в качестве простого решения, можно просто дать пользователю возможность ввести дату самому в текстовое поле. И в общем-то нет, наверное, ничего плохого в таком решении. Но иногда просто хочется сделать немного более красиво. В этом смысле &lt;a href=&quot;http://jqueryui.com/datepicker/#dropdown-month-year&quot;&gt;Datepicker&lt;/a&gt; из &lt;b&gt;jQuery UI&lt;/b&gt; вполне себе красив и позволяет выбирать год и месяц с помощью выпадающих списков, что очень сокращает путь до необходимой даты.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirS34HxtNCmrt3Lzp2xbL1u4stdN0GSCGhmEFllparY09Qe0cED74kMzATCf5Rz1wp6K4eaTu9vL1Ga5r5mq2KbfS-tHPkBncN_qe-vUz5ttc9WOVpwhur5DUylk9wX4ndwPt-2tNtkesR/s1600/jquery-date-picker.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirS34HxtNCmrt3Lzp2xbL1u4stdN0GSCGhmEFllparY09Qe0cED74kMzATCf5Rz1wp6K4eaTu9vL1Ga5r5mq2KbfS-tHPkBncN_qe-vUz5ttc9WOVpwhur5DUylk9wX4ndwPt-2tNtkesR/s1600/jquery-date-picker.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;jQuery Datepicker&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
На самом деле, такую же возможность очень несложно реализовать и в GWT DatePicker. Для этого нужно просто написать свою собственную реализацию абстрактного класса &lt;b&gt;MonthSelector&lt;/b&gt;, который представляет из себя визуальный компонент, отвечающий за установку текущего года и месяца в DatePicker.&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Немного теории &lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
MonthSelector расширяет класс &lt;b&gt;DatePickerComponent&lt;/b&gt;, у которого, в свою очередь, есть метод&lt;span style=&quot;font-family: inherit;&quot;&gt; &lt;b&gt;getModel()&lt;/b&gt;&lt;/span&gt;, возвращающий экземпляр класса &lt;b&gt;CalendarModel&lt;/b&gt;. Как вы уже поняли, экземпляр CalendarModel является моделью календаря DatePicker&#39;а и, получив его в своё распоряжение, мы получаем возможность управлять этим календарём. Пожалуй, самое интересное в модели для нас это методы &lt;b&gt;setCurrentMonth &lt;/b&gt;и &lt;b&gt;getCurrentMonth&lt;/b&gt;, которые позволяют установить текущий отображаемый месяц календаря и получить его, соответственно. Это в общем-то основное знание, которое необходимо для создания собственного MonthSelector, но есть ещё одна тонкость. Установить свой собственный MonthSelector в DatePicker можно только через &lt;b&gt;protected&lt;/b&gt;-конструктор DatePicker&#39;а. Поэтому нужно ещё расширить класс DatePicker и в своей реализации календаря установить свой свежеприготовленный MonthSelector, воспользовавшись этим защищённым конструктором. В данном конструкторе, кроме сохранения ссылки на экземпляр MonthSelector, происходит так же вызов метода &lt;b&gt;monthSelector.setDatePicker(this)&lt;/b&gt;, т. е. в наш новый MonthSelector передаётся ссылка на использующий его экземпляр DatePicker. Именно поэтому внутри нашего MonthSelector мы имеем доступ к модели DatePicker&#39;а.&lt;/div&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Ещё немного теории&lt;/span&gt; &lt;br /&gt;
&lt;br /&gt;
Как было сказано выше, всё что нужно для управления содержимым DatePicker&#39;а, это получить доступ к его модели CalendarModel. Теперь разберёмся, что нужно для того, чтобы DatePicker смог отобразить наш MonthSelector внутри себя. А нужно для этого совсем немного – состряпать визуальный компонент, представляющий MonthSelector, из каких-либо потомков класса &lt;b&gt;Widget&lt;/b&gt; и передать его в унаследованный от класса &lt;b&gt;Composite&lt;/b&gt; метод &lt;b&gt;initWidget(Widget widget)&lt;/b&gt;. Например, для нашего варианта MonthSelector будет состоять из двух ListBox&#39;ов и контейнера для них – экземпляра класса &lt;b&gt;Grid&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Практика&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Итак, расширяем стандартный класс DatePicker и добавляем в него новые методы для настройки поведения. Предлагаю добавить два новых метода setFloatingYearsRange и setFixedYearsRange. Первый метод будет устанавливать плавающий диапазон годов, а второй фиксированный. Плавающий диапазон будет отличаться от фиксированного тем, что при выборе нового, года список будет перестраиваться заново, таким образом, что пользователь сможет выбрать любой год, но при этом, количество годов в выпадающем списке будет постоянным. Если мысль моя изложена не слишком ясно, проще посмотреть аналогичное поведение &lt;a href=&quot;http://jqueryui.com/datepicker/#dropdown-month-year&quot;&gt;Datepicker&lt;/a&gt; из jQuery UI.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public class ListBoxDatePicker extends DatePicker {
     
    /**
    * Sets the floating range of years
    * 
    * @param negativeShift - shift to the left relative to 
    *                        the selected year; must be &amp;lt; 0.
    * @param positiveShift - shift to the right relative to 
    *                        the selected year; must be &amp;gt; 0.
    */
    public void setFloatingYearsRange(int negativeShift, int positiveShift) {}
        
    /**
    * Sets the fixed range of years
    * 
    * @param first - value of the first year in the list; 
    *                must be &amp;gt; 0 and &amp;lt;= last.
    * @param last  - value of the last year in the list; 
    *                must be &amp;gt; 0 and &amp;gt;= first.
    */
    public void setFixedYearsRange(int first, int last) {}

}
&lt;/pre&gt;
&lt;br /&gt;
В качестве параметров метод setFloatingYearsRange принимает отрицательное и положительное смещения относительно выбранного года. Если методу передать параметры -3 и 5, то при выборе 2013 года, список будет содержать года от 2010 до 2018 включительно, а при изменении года на 2010 список будет перестроен и будет содержать уже года с 2007 по 2015.&lt;br /&gt;
&lt;br /&gt;
В качестве параметров метод setFixedYearsRange принимает первый и последний год списка.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
С классом ListBoxDatePicker пока всё и в будущем он станет не намного сложнее, т. к. весь основной код будет в классе, который расширяет MonthSelector. Вот он с полями, которые нам понадобятся.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public class ListBoxMonthSelector extends MonthSelector {

    private static enum YearsRangeType {Fixed, Floating};
    // current type of the years range
    private YearsRangeType yearsRangeType;

    private static final DateTimeFormat monthFormat = 
                             DateTimeFormat.getFormat(&quot;yyyy-MMM&quot;);
    private static final DateTimeFormat yearFormat = 
                             DateTimeFormat.getFormat(&quot;yyyy&quot;);

    private String[] monthNames; // list of month names
    private String[] years;      // list of years available for selection

    private final ListBox yearsBox = new ListBox(false);
    private final ListBox monthsBox = new ListBox(false);

    // current shifts for floating range
    private int negativeYearShift = -1, positiveYearShift = -1;

    private Grid grid;      
}
&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Кратко о полях&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Т. к. наличие двух методов настройки нового DatePicker&#39;а предполагают различное поведение (фиксированный диапазон годов и плавающий), то надо иметь признак для понимания того, на какое поведение в данный момент настроен календарь. При двух вариантах можно использовать переменную &lt;b&gt;boolean&lt;/b&gt;, но мне более по душе &lt;b&gt;enum&lt;/b&gt;, т.к. если завтра появится новый вариант поведения, его признак будет проще добавить. Итак, &lt;b&gt;enum YearsRangeType {Fixed, Floating}&lt;/b&gt; служит для определения поведения календаря, а поле &lt;b&gt;yearsRangeType&lt;/b&gt; хранит текущее поведение. &lt;br /&gt;
&lt;br /&gt;
Поля&amp;nbsp; класса &lt;b&gt;monthFormat&lt;/b&gt; и &lt;b&gt;yearFormat&lt;/b&gt; будут использоваться для форматирования значений списков годов и месяцев в строку и обратно.&lt;br /&gt;
&lt;br /&gt;
Массивы строк &lt;b&gt;monthNames&lt;/b&gt; и &lt;b&gt;years&lt;/b&gt;, будут хранить значения месяцев и годов, на основе которых будут строиться выпадающие списки.&lt;br /&gt;
&lt;br /&gt;
Экземпляры ListBox &lt;b&gt;yearsBox&lt;/b&gt; и &lt;b&gt;monthsBox&lt;/b&gt; это те выпадающие списки, которые будут отображаться в верхней части DatePicker&#39;а для выбора года и месяца.&lt;br /&gt;
&lt;br /&gt;
В полях &lt;b&gt;negativeYearShift&lt;/b&gt; и &lt;b&gt;positiveYearShift&lt;/b&gt; будут храниться текущие значения смещений для плавающего диапазона годов, а при фиксированном пускай ровняются -1.&lt;br /&gt;
&lt;br /&gt;
Поле &lt;b&gt;grid&lt;/b&gt; это именно тот компонент, который будет отображаться в качестве MonthSelector в новом DatePicker&#39;е. Он будет содержать в себе компоненты yearsBox и monthsBox. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Основные методы&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Начнём сразу с самого главного метода в нашем новом MonthSelector&#39;е.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;protected void setYearsRange(int first, int last, YearsRangeType type){
    yearsRangeType = type;
    
    switch (type) {
    
    case Fixed:
        
        this.negativeYearShift = -1;
        this.positiveYearShift = -1;
        
        buildYears(first, last);

        break;
        
    case Floating:
        // various signs for left and right shifts 
        // are needed for additional verification
        if (first &amp;gt;= 0) throw 
            new IllegalArgumentException(&quot;First year shift value must be &amp;lt; 0&quot;);
        if (last &amp;lt;= 0) throw 
            new IllegalArgumentException(&quot;Last year shift value must be &amp;gt; 0&quot;);
        
        this.negativeYearShift = Math.abs(first);
        this.positiveYearShift = last;

        int currentYear = Integer.parseInt(getCurrentModelYear());
        buildYearsByShifts(currentYear, negativeYearShift, positiveYearShift);

        break;
    }

    updateYearsBox();
    setModelByListBoxes();
}
&lt;/pre&gt;
&lt;br /&gt;
Этот метод осуществляет настройку ListBoxMonthSelector - устанавливает диапазон доступных для выбора годов в выпадающем списке и определяет поведение этого списка (фиксированный или плавающий). Именно этот метод будет вызываться ListBoxDatePicker&#39;ом в методах setFloatingYearsRange и setFixedYearsRange. Заметьте, что первые два параметра зависят от третьего. Т. е. для YearsRangeType.Fixed данные параметры являются первым и последним годами в списке, а для Floating левым и правым смещением относительно базового года, соответственно. Вообще говоря, это плохая затея делать такое &quot;два в одном&quot;, но т. к. метод setYearsRange мы закрыли от клиентов модификатором protected, то можно немножко и согрешить :)&lt;br /&gt;
&lt;br /&gt;
Внутри метода, в зависимости от полученного YearsRangeType, происходит генерация массива (методы &lt;b&gt;buildYears&lt;/b&gt; и &lt;b&gt;buildYearsByShifts&lt;/b&gt;), содержащего список годов, которые должны быть отражены в соответствующем ListBox&#39;е. После этого, с помощью метода &lt;b&gt;updateYearsBox&lt;/b&gt; происходит построение ListBox&#39;а по сгенерированному массиву и установка значения модели по выбранным значениям в ListBox&#39;ах (метод &lt;b&gt;setModelByListBoxes&lt;/b&gt;).&lt;u&gt;&lt;b&gt; &lt;/b&gt;&lt;/u&gt;&lt;br /&gt;
&lt;br /&gt;
Метод setModelByListBoxes очень прост. Берём значения года и месяца из ListBox&#39;ов и с помощью DateTimeFormat создаём экземпляр Date, который используем для установки текущего отображаемого месяца нашего календаря. После изменения модели необходимо вызвать метод &lt;b&gt;refreshAll&lt;/b&gt;, чтобы DatePicker перерисовал календарь в соответствии с новым значением модели.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;private void setModelByListBoxes() {
    String year = yearsBox.getItemText(yearsBox.getSelectedIndex());
    String month = monthsBox.getItemText(monthsBox.getSelectedIndex());

    getModel().setCurrentMonth(
            monthFormat.parse(year + &quot;-&quot; + month));
    refreshAll();
}
&lt;/pre&gt;
&lt;br /&gt;
Если пользователь выберет новое значение в каком-то из наших списков, то нужно будет просто вызвать этот метод для изменения текущего отображаемого месяца DatePicker&#39;а. Но, кроме пользователя, значение текущего месяца календаря может изменить класс-клиент, т. к. из экземпляра DatePicker&#39;а доступен публичный метод setCurrentMonth, который вызывает одноименный метод модели и обновляет все свои компоненты, в том числе и MonthSelectror. Поэтому в дополнение к методу установки текущего месяца по значениям выпадающих списков, нам также нужен обратный метод, который выбирает соответствующие значения в ListBox&#39;ах, если значение текущего месяца модели календаря изменилось программно.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;private void setListBoxesByModel() {

    String currentMonth = monthFormat.
                            format(getModel().getCurrentMonth());
    
    String[] yearAndMonth = currentMonth.split(&quot;-&quot;);

    boolean yearSetted = false;
    boolean monthSetted = false;
    
    for (int i = 0; i &amp;lt; yearsBox.getItemCount(); i++) {
        if (yearsBox.getItemText(i).equals(yearAndMonth[0])) {
            yearsBox.setSelectedIndex(i);
            yearSetted = true;
            break;
        }
    }

    for (int i = 0; i &amp;lt; monthsBox.getItemCount(); i++) {
        if (monthsBox.getItemText(i).equalsIgnoreCase(yearAndMonth[1])) {
            monthsBox.setSelectedIndex(i);
            monthSetted = true;
            break;
        }
    }

    if(!yearSetted || !monthSetted)
        throw new RuntimeException(&quot;Can&#39;t set month &quot; + currentMonth + 
                                   &quot; in &quot; + getClass().getName());
}
&lt;/pre&gt;
&lt;br /&gt;
Для сравнения названий месяцев используется equalsIgnoreCase, т. к. я заметил, что для русской локали DateTimeFormat возвращает короткие названия месяцев (формат &quot;MMM&quot;) с маленькой буквы, а названия в массиве monthNames и, соответственно, в списке monthsBox начинаются с заглавной буквы. Такими их возвращает метод &lt;b&gt;monthsShortStandalone&lt;/b&gt; из интерфейса &lt;b&gt;DateTimeFormatInfo&lt;/b&gt;, который мы будем использовать ниже для инициализации списка месяцев. В детали этого метода я пока не вникал.&lt;br /&gt;
&lt;br /&gt;
В конце метода происходит проверка удалось ли установить месяц и год в списках, которые соответствуют значению месяца модели. Если нет, бросается RuntimeException. Такая ситуация может произойти, например, если в модель устанавливается значение года, которое не существует в выпадающем списке. Это место надо по-хорошему доработать и в целом предусмотреть другое поведение для таких случаев.&lt;br /&gt;
&lt;br /&gt;
Как я уже писал выше, клиенты могут изменить месяц календаря через публичный метод DatePicker&#39;а. В этом методе после изменения модели происходит обновление всех компонентов DatePicker&#39;а. Поэтому, для того что бы установить соответствующие значения в списках при изменении модели, нам надо переопределить метод &lt;b&gt;refresh&lt;/b&gt; нашего ListBoxMonthSelector и установить в нем новые значения в ListBox&#39;ах. Именно этот метод будет вызываться DatePicker&#39;ом после обновления модели.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;@Override
protected void refresh() {
    setListBoxesByModel();
}
&lt;/pre&gt;
&lt;br /&gt;
Итак, я описал основные рабочие методы из которых должно стать ясно, как работать с моделю календаря из MonthSelector. Остальные служебные методы вроде buildYears и buildYearsByShifts я описывать не буду, а просто приведу в конце статьи полный код с комментариями на моём корявом индокитайском диалекте английского языка :) Код хранится на гитхабе, поэтому я стараюсь там держать околоанглийские комментарии, вдруг кому пригодится.&lt;br /&gt;
&lt;br /&gt;
Разберемся теперь как и самое главное когда инициализировать компоненты нашего нового MonthSelector. Для удобства восприятия разделим инициализацию на три этапа: инициализация списка годов, инициализация списка месяцев и инициализация всего компонента в целом.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Инициализация списка годов&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Заставляем выпадающий список годов показывать только один выбранный элемент и навешиваем на него обработчик события, которое будет происходить при выборе пользователем какого-либо года в списке. При срабатывании события, если список плавающий, то надо перестроить весь список годов, а если нет, то список годов строится единожды при вызове метода setYearsRange. По крайней мере пока оставим поведение именно таким. После перестроения изменяем модель календаря в соответствии установленным значением. &lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;private void initYearsBox() {
    yearsBox.setVisibleItemCount(1);
    yearsBox.addChangeHandler(new ChangeHandler() {
        @Override
        public void onChange(ChangeEvent event) {
            if(yearsRangeType == YearsRangeType.Floating) {
                buildYearsByShifts(Integer.parseInt(getSelectedYear()), 
                                   negativeYearShift, positiveYearShift);
                updateYearsBox();
            }
            setModelByListBoxes();
        }
    });
}
&lt;/pre&gt;
&lt;br /&gt;
&lt;b&gt;Инициализация списка месяцев&lt;/b&gt; &lt;br /&gt;
&lt;br /&gt;
Сначала получаем локализованные короткие названия месяцев. Кстати, для русской локали названия слегка странные, на мой взгляд. Например, &quot;Февр.&quot; или &quot;Нояб.&quot;&amp;nbsp; вместо привычных &quot;Фев&quot; и &quot;Ноя&quot; без точки. После заполнения массива названий месяцев, так же как и в предыдущем методе, устанавливаем один отображаемый элемент для списка месяцев и создаем обработчик для события изменения выбранного месяца и в нём только обновляем модель календаря.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;private void initMonthsBox() {
    // localized short month names
    monthNames = LocaleInfo.getCurrentLocale().
                    getDateTimeFormatInfo().monthsShortStandalone();
    for (String month : monthNames) {
        monthsBox.addItem(month);
    }
    
    monthsBox.setVisibleItemCount(1);
    monthsBox.addChangeHandler(new ChangeHandler() {
        @Override
        public void onChange(ChangeEvent event) {
            setModelByListBoxes();
        }
    });
}
&lt;/pre&gt;
&lt;br /&gt;
&lt;b&gt;Инициализация MonthSelector&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Как уже, отмечалось, основой для визуальной части MonthSelector у нас будет компонент Grid. Создаём его размером 1 на 2 и помещаем в него компоненты список годов и список месяцев. Добавляем на всякий случай уникальное имя класса стиля и передаём созданный компонент в метод initWidget. После выполнения initWidget, DatePicker сможет отобразить внутри себя наш новый MonthSelector. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;private void initMonthSelector(){
    grid = new Grid(1, 2);
    grid.setWidget(0, 0, yearsBox);
    grid.setWidget(0, 1, monthsBox);
    grid.setStyleName(&quot;ListBoxMonthSelector&quot;);

    initWidget(grid);
}
&lt;/pre&gt;
&lt;br /&gt;
Необходимые методы инициализации созданы. Когда же их теперь вызвать? В принципе, в нашем случае эти три метода можно вызвать в конструкторе ListBoxMonthSelector, но лучше всего воспользоваться переопределённым методом &lt;b&gt;setup&lt;/b&gt;, который будет автоматически вызван при создании экземпляра DatePicker. Преимущество использования данного метода для инициализации компонента перед конструктором заключается в том, что в момент выполнения setup, вам уже доступен экземпляр DatePicker&#39;а и, соответственно, его модель, которые могут пригодиться для инициализации. Кроме инициализации компонентов, нам ещё нужно определить настройки по умолчанию для ListBoxMonthSelector. Сделаем это с помощью метода setYearsRange. И вот этот метод уже не может быть использован в конструкторе, т.к. внутри него происходит обращение к модели календаря, которая в конструкторе нам ещё не доступна.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;@Override
protected void setup() {
    initYearsBox();
    initMonthsBox();
    initMonthSelector(); 
    setYearsRange(-7, 7, YearsRangeType.Floating);
}
&lt;/pre&gt;
&lt;br /&gt;
По большому счёту, все необходимые шаги для создания собственного MonthSelector я описал. Теперь приведу полный код ListBoxMonthSelector.&lt;br /&gt;
&lt;br /&gt;
&lt;script class=&quot;brush: java&quot; type=&quot;syntaxhighlighter&quot;&gt;&lt;![CDATA[
package ru.eqlbin.gwt.datepicker.client;

import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.i18n.shared.DateTimeFormat;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.datepicker.client.MonthSelector;

/**
 * Extends 
 * {@link com.google.gwt.user.datepicker.client.MonthSelector MonthSelector}
 * from GWT library, which is set as component by default 
 * for {@link com.google.gwt.user.datepicker.client.DatePicker DatePicker}. 
 * 
 * Allows to select the current month of the
 * {@link com.google.gwt.user.datepicker.client.DatePicker DatePicker},
 * using the drop-down list 
 * {@link com.google.gwt.user.client.ui.ListBox ListBox}
 * for setting the month and year.
 * 
 * @author eqlbin
 *
 */
public class ListBoxMonthSelector extends MonthSelector {

    /**
     * Type of the year range for for {@link ListBoxMonthSelector}.

* 
     * &lt;b&gt;Fixed&lt;/b&gt;    - fixed range
* &lt;b&gt;Floating&lt;/b&gt; - floating range, which may vary 
     *                   according to the selected year
     */
    public static enum YearsRangeType {Fixed, Floating};
    // current type of the years range
    private YearsRangeType yearsRangeType;

    private static final DateTimeFormat monthFormat = 
                             DateTimeFormat.getFormat(&quot;yyyy-MMM&quot;);
    private static final DateTimeFormat yearFormat = 
                             DateTimeFormat.getFormat(&quot;yyyy&quot;);

    private String[] monthNames; // list of month names
    private String[] years;      // list of years available for selection

    private final ListBox yearsBox = new ListBox(false);
    private final ListBox monthsBox = new ListBox(false);

    // current shifts for floating range
    private int negativeYearShift = -1, positiveYearShift = -1;

    private Grid grid;       
    
    
    @Override
    protected void setup() {
        initYearsBox();
        initMonthsBox();
        initMonthSelector(); 
        setYearsRange(-7, 7, YearsRangeType.Floating);
    }

    @Override
    protected void refresh() {
        setListBoxesByModel();
    }

    /**
     * Initializes the {@link #yearsBox}
     */
    private void initYearsBox() {
        yearsBox.setVisibleItemCount(1);
        yearsBox.addChangeHandler(new ChangeHandler() {
            @Override
            public void onChange(ChangeEvent event) {
                if(yearsRangeType == YearsRangeType.Floating) {
                    buildYearsByShifts(Integer.parseInt(getSelectedYear()), 
                                       negativeYearShift, positiveYearShift);
                    updateYearsBox();
                }
                setModelByListBoxes();
            }
        });
    }

    /**
     * Initializes the {@link #monthsBox}
     */
    private void initMonthsBox() {
        // localized short month names
        monthNames = LocaleInfo.getCurrentLocale().
                        getDateTimeFormatInfo().monthsShortStandalone();
        for (String month : monthNames) {
            monthsBox.addItem(month);
        }
        
        monthsBox.setVisibleItemCount(1);
        monthsBox.addChangeHandler(new ChangeHandler() {
            @Override
            public void onChange(ChangeEvent event) {
                setModelByListBoxes();
            }
        });
    }

    /**
     * Initializes the {@link ListBoxDatePicker}.
     */
    private void initMonthSelector(){
        grid = new Grid(1, 2);
        grid.setWidget(0, 0, yearsBox);
        grid.setWidget(0, 1, monthsBox);
        grid.setStyleName(&quot;ListBoxMonthSelector&quot;);
    
        initWidget(grid);
    }
        
    /**
     * Sets the range of years available for selection 
     * in the date picker, and its behavior. 
     * 
     * @param first - the value of the first year on the list, 
     *                if the type of the range is fixed, 
     *                or shift to the left of the currently 
     *                selected year if the range is floating;
     *                for a fixed range value must be &gt; 0 and 
     *                &lt;= last, but &lt; 0 if range is floating
     *                
     * @param last -  the value of the last year on the list, 
     *                if the type of the range is fixed,  
     *                or shift to the right of the currently 
     *                selected year if the range is floating;
     *                for a fixed range value must be &gt; 0 and 
     *                &gt;= first, but &gt; 0 if range is floating
     *                 
     * @param type -  range type that defines its behavior
     * 
     * @see YearsRangeType
     */
    protected void setYearsRange(int first, int last, YearsRangeType type){
        yearsRangeType = type;
        
        switch (type) {
        
        case Fixed:
            
            this.negativeYearShift = -1;
            this.positiveYearShift = -1;
            
            buildYears(first, last);
    
            break;
            
        case Floating:
            // various signs for left and right shifts 
            // are needed for additional verification
            if (first &gt;= 0) throw 
                new IllegalArgumentException(&quot;First year shift value must be &lt; 0&quot;);
            if (last &lt;= 0) throw 
                new IllegalArgumentException(&quot;Last year shift value must be &gt; 0&quot;);
            
            this.negativeYearShift = Math.abs(first);
            this.positiveYearShift = last;
    
            int currentYear = Integer.parseInt(getCurrentModelYear());
            buildYearsByShifts(currentYear, negativeYearShift, positiveYearShift);
    
            break;
        }
    
        updateYearsBox();
        setModelByListBoxes();
    }

    /**
     * Rebuilds the component {@link #yearsBox}
     * according with the current value of the 
     * field {@link #years}
     */
    private void updateYearsBox() {
        
        // save the selected year
        String selectedYear = getSelectedYear();
        boolean selectedFound = false;

        this.yearsBox.clear();
        for (int i=0; i&lt;this.years.length; i++) {
            String year = this.years[i];
            this.yearsBox.addItem(year);
            
            if(year.equals(selectedYear)) {
                this.yearsBox.setSelectedIndex(i);
                selectedFound = true;
            }
        }
        
        if(selectedFound) return;
        
        // select the middle item if the previous selected year is not found in yearsBox
        this.yearsBox.setSelectedIndex(this.yearsBox.getItemCount()/2);
    }
            
    /**         
     * Generates and stores in a field {@link #years}
     * the list of years. Based on shifts relative to the base year.
     * 

* For example, to create a list of years from 1990 to 2005, inclusive:
     * 

* {@code buildYearsByShifts(2000, 10, 5);}
     * 
     * @param baseYear - base year; must be &gt; 0.
     * 
     * @param negativeShiftYear - shift to the left relative to the base year;
     *                            must be &gt; 0.
     * @param positiveShiftYear - shift to the right relative to the base year; 
     *                            must be &gt; 0.
     */
    private void buildYearsByShifts(int baseYear, 
            int negativeShiftYear, int positiveShiftYear){
        
        if (baseYear &lt; 0 || negativeShiftYear &lt; 0 || positiveShiftYear &lt; 0)
            throw new IllegalArgumentException(&quot;All arguments must be positive values&quot;);
        
        // cut off the possible negative years, 
        // recalculating the negativeShiftYear
        if(baseYear &lt;= negativeShiftYear)
            negativeShiftYear = negativeShiftYear - (negativeShiftYear - baseYear) - 1;

        int yearsCount = positiveShiftYear + negativeShiftYear + 1;
        int firstYear = baseYear - negativeShiftYear;
    
        this.years = new String[yearsCount];

        for (int i = firstYear; i &lt; firstYear + yearsCount; i++) {
                this.years[i - firstYear] = yearToString(i);
        } 
    }

    /**
     * Generates and stores in a field {@link #years}
     * a list of years from first to last, inclusive
     * 
     * @param first - first year in the list
     * @param last  - last year in the list
     */
    private void buildYears(int first, int last){
        
        if (last &lt; first) throw 
            new IllegalArgumentException(&quot;Last year must be &gt;= first year&quot;);
        if (first &lt; 1 || last &lt; 1) throw 
            new IllegalArgumentException(&quot;Years must be &gt; 0&quot;);

        this.years = new String[last - first + 1];

        for (int i = first; i &lt;= last; i++) {
                this.years[i - first] = yearToString(i);
        }
    }

    /**
     * Sets the values ​​in the components {@link #yearsBox}
     * and {@link #monthsBox}, which correspond to the current month of the 
     * {@link com.google.gwt.user.datepicker.client.CalendarModel CalendarModel}
     */
    private void setListBoxesByModel() { 

        String currentMonth = monthFormat.
                                format(getModel().getCurrentMonth());
        
        String[] yearAndMonth = currentMonth.split(&quot;-&quot;);
    
        
        boolean yearSetted = false;
        boolean monthSetted = false;
        
        for (int i = 0; i &lt; yearsBox.getItemCount(); i++) {
            if (yearsBox.getItemText(i).equals(yearAndMonth[0])) {
                yearsBox.setSelectedIndex(i);
                yearSetted = true;
                break;
            }
        }
    
        for (int i = 0; i &lt; monthsBox.getItemCount(); i++) {
            if (monthsBox.getItemText(i).equalsIgnoreCase(yearAndMonth[1])) {
                monthsBox.setSelectedIndex(i);
                monthSetted = true;
                break;
            }
        }
    
        if(!yearSetted || !monthSetted)
            throw new RuntimeException(&quot;Can&#39;t set month &quot; + currentMonth + 
                                       &quot; in &quot; + getClass().getName());
    }
    
    /**
     * Sets the current calendar month, based on the values 
     * ​​that are specified in the components {@link #yearsBox}
     * и {@link #monthsBox}
     */
    private void setModelByListBoxes() {
        String year = yearsBox.getItemText(yearsBox.getSelectedIndex());
        String month = monthsBox.getItemText(monthsBox.getSelectedIndex());
    
        getModel().setCurrentMonth(
                monthFormat.parse(year + &quot;-&quot; + month));
        refreshAll();
    }

    /**
     * Returns current selected year in 
     * the {@link #yearsBox} as String
     * 
     * @return current selected year
     */
    private String getSelectedYear(){
        String selectedYear = null;
        int selectedIndex = yearsBox.getSelectedIndex();
        
        if(selectedIndex &gt;= 0)
            selectedYear = yearsBox.getItemText(selectedIndex);
        
        return selectedYear;
    }
    
    /**
     * Returns current year of the 
     * {@link com.google.gwt.user.datepicker.client.CalendarModel CalendarModel}
     * as String
     * 
     * @return current year of the DatePicker model
     */
    private String getCurrentModelYear(){
        return yearFormat.format(getModel().getCurrentMonth());
    }
    
    
    /**
     * Converts the value at the string so that the number 
     * of characters is always equal to 4. 
     * 
     * For example, the year of 15 will be converted to a string 0015.
     * 
     * @param year - year to translate
     * @return year as String
     */
    private static String yearToString(int year){      
        if(year &lt; 1000) 
            return NumberFormat.getFormat(&quot;0000&quot;).format(year);
        
        return String.valueOf(year);
    }
}
]]&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
И теперь полный код ListBoxDatePicker.&lt;br /&gt;
&lt;br /&gt;
&lt;script class=&quot;brush: java&quot; type=&quot;syntaxhighlighter&quot;&gt;&lt;![CDATA[
package ru.eqlbin.gwt.datepicker.client;

import com.google.gwt.user.datepicker.client.CalendarModel;
import com.google.gwt.user.datepicker.client.DatePicker;
import com.google.gwt.user.datepicker.client.DefaultCalendarView;

/**
 * Date picker component which allows to select the current month of the calendar,
 * using the drop-down list for setting the month and year.
 * 
 * @author eqlbin
 *
 */
public class ListBoxDatePicker extends DatePicker {

    public ListBoxDatePicker() {
        super(new ListBoxMonthSelector(), new DefaultCalendarView(),
                new CalendarModel());
    }
  
    /**
     * Sets the floating range of years
     * 
     * @param negativeShift - shift to the left relative to 
     *                        the selected year; must be &lt; 0.
     * @param positiveShift - shift to the right relative to 
     *                        the selected year; must be &gt; 0.
     */
    public void setFloatingYearsRange(int negativeShift, int positiveShift) {
        ((ListBoxMonthSelector)getMonthSelector())
                    .setYearsRange(negativeShift, positiveShift, 
                         ListBoxMonthSelector.YearsRangeType.Floating);
        ((DefaultCalendarView)getView()).refresh();
    }
    
    /**
     * Sets the fixed range of years
     * 
     * @param first - value of the first year in the list; 
     *                must be &gt; 0 and &lt;= last.
     * @param last  - value of the last year in the list; 
     *                must be &gt; 0 and &gt;= first.
     */
    public void setFixedYearsRange(int first, int last) {
        ((ListBoxMonthSelector)getMonthSelector())
                    .setYearsRange(first, last, 
                          ListBoxMonthSelector.YearsRangeType.Fixed);
        ((DefaultCalendarView)getView()).refresh();
    }
}
]]&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что каждый метод установки диапазона годов в ListBoxDatePicker заканчивается вызовом метода refresh для &lt;b&gt;DefaultCalendarView&lt;/b&gt;. Этим мы сигнализируем, что модель календаря изменилась и заставляем обновить его представление.&lt;br /&gt;
&lt;br /&gt;
Пример использования ListBoxDatePicker:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;// Fixed range of years
ListBoxDatePicker datePicker1 = new ListBoxDatePicker();
datePicker1.setFixedYearsRange(2005, 2015);

// Floating range of years
ListBoxDatePicker datePicker2 = new ListBoxDatePicker();
datePicker2.setFloatingYearsRange(-5, 5);
&lt;/pre&gt;
&lt;br /&gt;
А вот что получилось&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgotDrtpk9yfvhyphenhyphenWMj40eMg8eBeoBpA6rkKP153NGhTKQIhlEtHplReRu2DZeaWCRW1DC5e_fLJyyI64AxoD_crN9W7EgsgHocOgitV-RglZGgOTJDOIFBILv4EDb8i49ohoXtkGmAvMjeu/s1600/list-box-date-picker.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgotDrtpk9yfvhyphenhyphenWMj40eMg8eBeoBpA6rkKP153NGhTKQIhlEtHplReRu2DZeaWCRW1DC5e_fLJyyI64AxoD_crN9W7EgsgHocOgitV-RglZGgOTJDOIFBILv4EDb8i49ohoXtkGmAvMjeu/s1600/list-box-date-picker.png&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMfGxtLCbwQfG75bEWb77QSM4UZe31Y6AWgalurxN2atS52_FufallwhAVTGD_vHyc-AvQnxKOXTnTY4oOvOjbxTrnby6zTqUmJdy55uyAYexk_xfZlhjgVEKGKBC3W1eMGRi37Ptst7-H/s1600/list-box-date-picker-ru.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMfGxtLCbwQfG75bEWb77QSM4UZe31Y6AWgalurxN2atS52_FufallwhAVTGD_vHyc-AvQnxKOXTnTY4oOvOjbxTrnby6zTqUmJdy55uyAYexk_xfZlhjgVEKGKBC3W1eMGRi37Ptst7-H/s1600/list-box-date-picker-ru.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;br /&gt;
Созданный календарь является просто примером и не претендует на большую стабильность, поэтому без тщательной проверки и доработки пользоваться им для публичных проектов не рекомендую :)&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &lt;br /&gt;
Исходный код с примером доступен &lt;a href=&quot;https://github.com/eqlbin/gwt-listbox-datepicker&quot;&gt;тут&lt;/a&gt;. Там возможно, он будет обновляться и совершенствоваться, если мне это будет нужно.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/4310162396556111193/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2013/10/gwt-datepicker-listbox.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/4310162396556111193'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/4310162396556111193'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2013/10/gwt-datepicker-listbox.html' title='Выбор года и месяца в GWT DatePicker с помощью ListBox'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0GAa9KI7HsQj4EKu-AVxtXJ9KIm5TubqYK2fuZjSPG_nqr2-LBwPdjH36JSO6bxuj9WZqYidzXoxM1Jwq1AzobkaninICZjccp-CYKL77fxpte9FbUi4sfrOZIRn5wcOd53xJRIYvgyr4/s72-c/std-date-picker.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-8801390929646605515</id><published>2013-10-02T06:29:00.000+04:00</published><updated>2013-10-02T06:36:52.705+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bash"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="администрирование"/><title type='text'>Небезопасно посылаем e-mail из bash&#39;a используя сторонний smtp</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;Как известно, все настоящие админы лентяи, в хорошем смысле этого слова. По этой причине они стараются автоматизировать всё что можно и что нельзя. Одним из главных инструментов тотальной админоавтоматизации является ужасно прекрасный (или прекрасно ужасный) bash. Огромная куча поэтично сложенных bash-скриптов постоянно трудится без обеда, что бы админ мог изучить что-то новое или просто почитать Хабр :) Но бывает так, что случается беда и столкнувшийся с ней скрипт обязан призвать админа для спасения организации. Призывом админа проще, конечно же, заниматься посредством e-mail. Только не всегда есть желание ставить и настраивать какой-нибудь postfix на каждой машине, которая должна оповещать админа о проблемах. Иногда просто хочется одной командой послать письмо, использовав уже настроенный smtp организации. Сделать это можно при помощи программки msmtp. Я применяю этот подход только для использования smtp, находящегося внутри локальной сети организации, т.к. он не очень правильный с точки зрения безопасности, но в качестве примера покажу, как можно отправлять письма используя гугловский smtp.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;Пример:&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;#!/bin/bash

printf &quot;Subject:%b\nFrom:%b\nTo:%b\n%b\n&quot; \
       &quot;From good admin&quot; &quot;someuser@gmail.com&quot; &quot;ded@moroz.ru&quot; \
       &quot;Hello Ded Moroz! I worked hard all year, and so I want to get a red bike for the New Year!&quot;|\
msmtp -t --host=smtp.gmail.com \
      --port=587 \
      --user=someuser \
      --passwordeval=&quot;echo some_incredible_password&quot; \
      --from=someuser@gmail.com \
      --tls=on \
      --tls-certcheck=off \
      --auth=on 
&lt;/pre&gt;&lt;br /&gt;
Наверное, тут практически всё ясно и без слов, но немного поясню. Команда printf формирует само письмо, состоящее из заголовков Subject, From, To и тела письма, после чего вывод перенаправляется в команду msmtp, которая и отправляет письмо.&lt;br /&gt;
&lt;br /&gt;
Параметры msmtp:&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; -t - заставляет программу msmtp брать адреса назначения из заголовков To, Cc и Bcc&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; --host - smtp-сервер, который надо использовать для отправки&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; --port - порт smtp-сервера&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; --user - пользователь&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; --passwordeval - команда, которая выводит пароль на стандартный вывод&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; --from - отправитель письма&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; --tls - включает/выключает TLS/SSL шифрование&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; --tls-certcheck - включает/выключает проверку сертификата &lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; --auth - включает/выключает аутентификацию&lt;br /&gt;
&lt;div&gt;&lt;br /&gt;
Заголовки Subject, From и To можно и не создавать, просто в таком случае письмо будет без темы и поля To, а в качестве From будет установлено значение параметра  --from. Но так делать некрасиво :) &lt;br /&gt;
&lt;br /&gt;
Параметр -t тоже можно не использовать, а вместо него в качестве последних параметров через пробел указать адреса, на которые нужно отправить письмо.&lt;br /&gt;
&lt;br /&gt;
Если не указать параметр&lt;span style=&quot;font-family: inherit;&quot;&gt;&amp;nbsp; --passwordeval, то при запуске команды нужно будет ввести пароль руками.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Исходя из вышесказанного, можно переделать процедуру отправки письма как-нибудь так:&lt;br /&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;#!/bin/bash

printf &quot;%b\n&quot; \
       &quot;Hello Ded Moroz! I worked hard all year, and so I want to get a red bike for the New Year!&quot;|\
msmtp --host=smtp.gmail.com \
      --port=587 \
      --user=someuser  \
      --from=someuser@gmail.com \
      --tls=on \
      --tls-certcheck=off \
      --auth=on \
      ded@moroz.ru \
      santa@klaus.com
&lt;/pre&gt;&lt;br /&gt;
Как я уже сказал, это небезопасный приём, потому что пароль в первом варианте хранится в открытом виде в скрипте и в обоих вариантах не происходит проверки сертификата. При большом желании можно со всем этим заморочиться и сделать немного получше.&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/8801390929646605515/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2013/10/e-mail-basha-smtp.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/8801390929646605515'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/8801390929646605515'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2013/10/e-mail-basha-smtp.html' title='Небезопасно посылаем e-mail из bash&#39;a используя сторонний smtp'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-1074907283928880282</id><published>2013-08-01T06:13:00.004+04:00</published><updated>2013-08-01T06:13:33.046+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="NVIDIA"/><title type='text'>Патчим драйвер NVIDIA 319.32 для ядра Linux 3.10</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Обновил себе ядро Linux до версии 3.10 и, как это всегда бывает в таких случаях, после перезагрузки нужно было пересобрать драйвер NVIDIA. Однако, оказалось, что последний драйвер NVIDIA (на этот момент версии 319.32) не поддерживает моё новое ядро. Вариантов в таком случае два: даунгрэйдить ядро до 3.9 или патчить драйвер NVIDIA (если есть чем патчить). Так уж сложилось, что даунгрэйдить я очень не люблю, а вот к добрым ребятушкам, которые в свободное время пописывают всякие патчи и улучшают наш тленный мирок, испытываю самые тёплые и нежные чувства. Конечно же, патч решающий мою проблему, был уже давно написан и выложен, за что автору большое спасибо. Осталось только разобраться, как его использовать. Разобрался. Делюсь.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://pastie.org/7942599&quot;&gt;Качаем патч&lt;/a&gt;, например, в домашний каталог. По умолчанию имя будет pastie-7942599.diff.&lt;br /&gt;
&lt;br /&gt;
Распаковываем все внутренности скрипта с драйвером:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt; NVIDIA-Linux-x86-319.32.run -x&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;Creating directory NVIDIA-Linux-x86-319.32&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;Verifying archive integrity... OK&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;Uncompressing NVIDIA Accelerated Graphics Driver for Linux-x86 319.32...&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Переходим в созданный каталог:&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/b&gt; &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt; cd NVIDIA-Linux-x86-319.32&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Патчим драйвер:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt; patch -p1 &amp;lt; /home/user/pastie-7942599.diff&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;patching file kernel/nv-i2c.c&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;patching file kernel/nv-procfs.c&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;Hunk #4 succeeded at 161 with fuzz 1.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Выходим из каталога с драйвером:&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&lt;br /&gt;
&lt;/span&gt; &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt; cd ..&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Запаковываем наши пропатченные кишочки обратно:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt; ./NVIDIA-Linux-x86-319.32/makeself.sh --target-os Linux --target-arch x86 NVIDIA-Linux-x86-319.32 NVIDIA-Linux-x86-319.32-patched-3.10.run &quot;NVIDIA Accelerated Graphics Driver patched for Linux-x86 3.10 kernel&quot; ./nvidia-installer&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Устанавливаем пропатченный драйвер, как обычно:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt; sh NVIDIA-Linux-x86-319.32-patched-3.10.run&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
У меня всё пропатчилось и собралось без проблем. Про качество работы пропатченного драйвера сказать пока что нечего, но иксы запустились, что уже прекрасно несомненно :)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/1074907283928880282/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2013/08/nvidia-31932-linux-310.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/1074907283928880282'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/1074907283928880282'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2013/08/nvidia-31932-linux-310.html' title='Патчим драйвер NVIDIA 319.32 для ядра Linux 3.10'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-2098343224364062909</id><published>2012-11-15T06:57:00.002+04:00</published><updated>2016-07-08T13:29:59.528+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Ant"/><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="libpcap"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="network"/><category scheme="http://www.blogger.com/atom/ns#" term="openSUSE"/><category scheme="http://www.blogger.com/atom/ns#" term="Программирование"/><title type='text'>Сборка jNetPcap из исходников в openSUSE 12.1</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Набрел тут на интересную java-обёртку для libpcap. Называется она &lt;a href=&quot;http://jnetpcap.com/&quot;&gt;jNetPcap&lt;/a&gt;. По описаниям и примерам выглядит достойно. Но главное, что в отличие от &lt;a href=&quot;http://jpcap.gitspot.com/download.html&quot;&gt;Jpcap&lt;/a&gt;, с которой я как-то давненько имел интимные отношения, jNetPcap продолжает развиваться.&lt;br /&gt;
&lt;br /&gt;
Если кто не в курсе, кратко поясню, что libpcap это библиотека для захвата и анализа пакетов, проходящих через сетевые интерфейсы. Её, например, используют такие программы, как wireshark, tcpdump, nmap, ipcad и многие другие.&lt;br /&gt;
&lt;br /&gt;
На сайте разработчиков, есть готовые сборки для Ubuntu, RedHat Enterprise, Fedora и Windows. Архив сборки содержит два интересующих меня файла jnetpcap.jar и libjnetpcap.so. Сборка для Fedora сомнительно, но гордо называется Linux (Generic). Сначала я попробовал именно её. При запуске примера она искала libpcap.so.0.9, но ничего не нашла, т.к. у меня стоит версия 1.3.0. В принципе, простой симлинк в /usr/lib libpcap.so.0.9 -&amp;gt; libpcap.so.1.3.0 решает проблему, и пример начинает работать, но что-то меня смущает такая большая разница в версиях libpcap. Поэтому пришлось собирать jNetPcap из исходников.&lt;br /&gt;
&lt;br /&gt;
Сборка прошла не совсем гладко, поэтому оставлю тут для себя, а может и ещё для кого, заметку поэтому поводу.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Теперь кратко о проблемах: &lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;
При попытке собрать jnetpcap.jar (команда ant build-jar):
&lt;pre class=&quot;brush: plain&quot;&gt;[taskdef] Could not load definitions from resource cpptasks.tasks. 
It could not be found.

[typedef] Could not load definitions from resource cpptasks.types.
It could not be found.

build.xml:119: taskdef class org.vafer.jdeb.ant.DebAntTask cannot be found 
using the classloader AntClassLoader[]
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
При попытке собрать libjnetpcap.so (команда ant comp-jni-linux):
&lt;pre class=&quot;brush: plain&quot;&gt;[cc] /home/eqlbin/SRC/jnetpcap-src-1.3.0-1/src/c/jnetpcap.cpp:996:6: 
error: invalid suffix &quot;xpackage&quot; on integer constant

[cc] &lt;command-line&gt;:0:1: error: &quot;not&quot; cannot be used as a macro name as it is an
operator in C++&lt;/command-line&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
Я, честно говоря, не совсем понял, как надо правильно настраивать сборку по мнению самих разработчиков, и в документации ничего путного не нашлось, а долго ковырять чужой билд-скрипт и проперти-файлы желания большого не было, поэтому проблемы решил так как получилось :)&lt;br /&gt;
&lt;br /&gt;
Для сборки, помимо JDK, понадобятся такие естественные штуки, как gcc, gcc-c++, libpcap-devel, ant, ant-junit, ant-contrib. Возможно я что-то упустил, но это основное.&lt;br /&gt;
&lt;br /&gt;
Поехали:
&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://sourceforge.net/projects/jnetpcap/files/jnetpcap/1.3/&quot;&gt;Качаем&lt;/a&gt; и распаковываем исходники jNetPcap в гипотетический каталог ~/jnetpcap-src&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://sourceforge.net/projects/ant-contrib/files/ant-contrib/&quot;&gt;Качаем cpptasks&lt;/a&gt; для ant и кладём файл cpptasks.jar например в ~/jnetpcap-src/lib&lt;/li&gt;
&lt;li&gt;Дальше, если у вас есть желание создать из всего хозяйства файл deb, то понадобится найти библиотеку &lt;a href=&quot;https://github.com/tcurdt/jdeb&quot;&gt;jdeb.jar&lt;/a&gt; с соответствующими тасками для ant. Её можно найти уже собранную или же собрать самому. У меня такого желания не образовалось, поэтому я просто&amp;nbsp; закомментировал строку в файле build.xml:
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;&lt;![CDATA[
&lt;taskdef name=&quot;deb&quot; classpathref=&quot;ant.deb.classpath&quot;
         classname=&quot;org.vafer.jdeb.ant.DebAntTask&quot; /&gt;
]]&gt;&lt;/script&gt;
&lt;/li&gt;
&lt;li&gt;Теперь необходимо подправить build.xml, чтобы ant смог найти cpptasks.jar и jdeb.jar (если вы, используете jdeb и не закомментировали строку, как в п.3):

Вот это:
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;&lt;![CDATA[
&lt;path id=&quot;ant.cpptask.classpath&quot; location=&quot;${lib.dir}/cpptasks-1.0b4/cpptasks.jar&quot; /&gt;
&lt;path id=&quot;ant.deb.classpath&quot; location=&quot;${lib.dir}/jdeb-0.6.jar&quot; /&gt;
]]&gt;&lt;/script&gt;
Надо привести к виду, вроде этого:
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;&lt;![CDATA[
&lt;path id=&quot;ant.cpptask.classpath&quot; location=&quot;${lib.dir}/cpptasks.jar&quot; /&gt;
&lt;path id=&quot;ant.deb.classpath&quot; location=&quot;${lib.dir}/jdeb.jar&quot; /&gt;
]]&gt;&lt;/script&gt;

Всё, конечно, зависит от того, как называются ваши jar-файлы и где они лежат... Короче, думаю, что и так тут всё ясно.
&lt;/li&gt;
&lt;li&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;Добавить в файл &lt;b&gt;Linux.properties&lt;/b&gt; строку &lt;b&gt;compiler.LIBPCAP_VERSION = 100&lt;/b&gt;&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;Запустить &lt;b&gt;ant clean comp-jni-linux build-jar&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Забрать из&amp;nbsp;~/jnetpcap-src/build/lib вожделенные  jnetpcap.jar и libjnetpcap.so (там еще у меня появился файл libjnetpcap.so.1.3.0, но судя по его размеру, это тот же &lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;libjnetpcap.so)&lt;/span&gt;&lt;br /&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
P.S. - Если build.xml кажется вам страшным и непонятным, почитайте &lt;a href=&quot;http://blog.eqlbin.ru/2011/09/apache-ant.html&quot;&gt;это&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/2098343224364062909/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2012/11/jnetpcap-opensuse-121.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/2098343224364062909'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/2098343224364062909'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2012/11/jnetpcap-opensuse-121.html' title='Сборка jNetPcap из исходников в openSUSE 12.1'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-2849817689790275006</id><published>2012-04-28T10:08:00.001+04:00</published><updated>2012-05-02T03:10:44.658+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Ant"/><category scheme="http://www.blogger.com/atom/ns#" term="Eclipse"/><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="Программирование"/><title type='text'>Автоматическое создание Runnable JAR в Eclipse и ресурсы</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Когда разрабатывается относительно небольшое десктопное приложение на Java, частенько возникает вполне, на мой взгляд, естественное желание запихать все классы, библиотеки и ресурсы в один исполняемый &lt;b&gt;JAR&lt;/b&gt;-файл. В некоторых случаях именно такое решение является предпочтительным.&lt;br /&gt;
&lt;br /&gt;
В &lt;b&gt;Eclipse IDE&lt;/b&gt; это можно сделать очень легко и, кроме того, автоматизировать это действие, чтобы в дальнейшем делать сборку JAR одной кнопкой.&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;



Сборка&amp;nbsp;&lt;/h2&gt;
Кликаем правой кнопкой по проекту, который надо упаковать в исполняемый JAR и нажимаем &lt;b&gt;Export&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
В окне выбираем&lt;b&gt; Java -&amp;gt; Runnable JAR &lt;/b&gt;и жмём &lt;b&gt;Next&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZBAUcx3MkK4txoamNP-ZkYk6lxrA0aJzKBwne2HMMYEIxgoHXjdQqfRUNv5Ao_IO2JlshbDvEWBuhRVrVBmLKMJcCkQ-BPFCyg40nle9pkpzdAKUU76b8IZSEtacTZlSZBiThyphenhyphenYWJFwoa/s1600/runnable_jar1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZBAUcx3MkK4txoamNP-ZkYk6lxrA0aJzKBwne2HMMYEIxgoHXjdQqfRUNv5Ao_IO2JlshbDvEWBuhRVrVBmLKMJcCkQ-BPFCyg40nle9pkpzdAKUU76b8IZSEtacTZlSZBiThyphenhyphenYWJFwoa/s1600/runnable_jar1.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Теперь настраиваем параметры создания JAR:&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Конфигурация запуска (&lt;b&gt;Launch configuration&lt;/b&gt; или &lt;b&gt;Run configuration&lt;/b&gt;), которая отвечает за запуск проекта. Из неё во время сборки будет взята информация о Main class, CLASSPATH и всех необходимых библиотеках&lt;/li&gt;
&lt;li&gt;Имя создаваемого JAR-файла&lt;/li&gt;
&lt;li&gt;Далее надо указать, как следует поступить с подключенными библиотеками:&lt;/li&gt;
&lt;ol&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Распаковать содержимое библиотек и запаковать и вместе с вашими классами и ресурсами в один JAR&lt;/li&gt;
&lt;li&gt; Не распаковывая положить все библиотеки в создаваемый JAR. В дополнение к этому Eclipse положит в build-катлог файл &lt;b&gt;jar-in-jar-loader.zip&lt;/b&gt;, который содержит классы, позволяющие использовать эти &quot;дважды упакованные&quot; библиотеки вашему приложению  (&lt;b&gt;Classloader&lt;/b&gt;). Эти классы будут также упакованы в ваш JAR и, следовательно, для его выполнения после окончательной сборки архив&lt;b&gt; &lt;/b&gt;jar-in-jar-loader.zip не нужен. Однако, для того чтобы можно было автоматизировать такую сборку, этот архив обязательно должен быть в build-каталоге, поэтому после первой &quot;кнопочно-окошечной&quot; сборки его удалять не стоит&lt;/li&gt;
&lt;li&gt;Положить все необходимые библиотеки в каталог рядом с созданным JAR&#39;ом &lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;


&lt;/ol&gt;
&lt;li&gt;Можно указать где сохранить сгенерированный Ant-скрипт для этой сборки. В дальнейшем его можно будет использовать для автоматизации процесса создания исполняемого JAR-файла из проекта&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfwVs3aMyKYcsFx4I751ID7HHU3x9oLX3qBt8zL-fWD6KGGCGKQ-kBL9jAogDqtwImVTxbRCEdXWUmZlM4liHn9c6-or_7XEUOQSkUSmV25tci73FKoRWDjgowPIfEVGUuf6azm12gyMLl/s1600/runnable_jar2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfwVs3aMyKYcsFx4I751ID7HHU3x9oLX3qBt8zL-fWD6KGGCGKQ-kBL9jAogDqtwImVTxbRCEdXWUmZlM4liHn9c6-or_7XEUOQSkUSmV25tci73FKoRWDjgowPIfEVGUuf6azm12gyMLl/s1600/runnable_jar2.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
При создании JAR, автоматом также будет создан файл &lt;b&gt;MANIFEST.MF&lt;/b&gt; с правильными &lt;b&gt;Class-Path&lt;/b&gt; и &lt;b&gt;Main-Class&lt;/b&gt;, поэтому ваш архив можно будет запустить простой командой &lt;b&gt;java -jar application.jar&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
Если вы решите упаковывать все библиотеки в один JAR вместе с вашими классами, &lt;u&gt;убедитесь, что лицензии библиотек это разрешают&lt;/u&gt;.&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;

Автоматизация сборки&lt;/h2&gt;
Как было сказано ранее, при создании JAR с помощью Eclipse, вы можете сохранить сгенерированный скрипт Ant, содержащий все необходимые действия.&lt;br /&gt;
&lt;br /&gt;
В свойствах проекта (&lt;b&gt;Project -&amp;gt; Properties&lt;/b&gt;) есть раздел &lt;b&gt;Builders&lt;/b&gt;, в котором вы можете создавать дополнительные билдеры и в частности таким билдером может быть Ant, которому вы и подсовываете этот файл. После этого при каждом запуске проекта (Ctrl + F11), JAR будет собираться автоматически, если были изменения в исходном коде.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghdZVNPAyzeXbyR-r6K_wyOIxIeSdg6iyLlzg1c8i6XhSb6swYB9uxZ-gxR-EWFxIA6oJdnlWHXH5G9KTlWRvw9HbXkWGDoSlkzpGllt2SBrWLyP_HoHSGjCi61bjj5Cn34cjjZB4-PcAL/s1600/runnable_jar3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghdZVNPAyzeXbyR-r6K_wyOIxIeSdg6iyLlzg1c8i6XhSb6swYB9uxZ-gxR-EWFxIA6oJdnlWHXH5G9KTlWRvw9HbXkWGDoSlkzpGllt2SBrWLyP_HoHSGjCi61bjj5Cn34cjjZB4-PcAL/s1600/runnable_jar3.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Такой вариант постоянной сборки JAR не особо удобен, т.к. может занимать некомфортное по продолжительности время. Вместо этого можно создать этот билдер в External Tools (&lt;b&gt;Run -&amp;gt; External Tools -&amp;gt; External Tools Configurations&lt;/b&gt;) и запускать сборку JAR только, когда вам это действительно нужно.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgr-kaX2ZtaP8DzS1NTQAsSpsVUZv5IXuZ66YqF3DhbahisQYNnb_aFAEbLa7koFJFuOW285R3ihdOPofvIucBtItc6UQASLCHLwyoHFyG21jZyq2xzoXxunKAjJK8VotL-SsYfgRZDFIts/s1600/runnable_jar4.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgr-kaX2ZtaP8DzS1NTQAsSpsVUZv5IXuZ66YqF3DhbahisQYNnb_aFAEbLa7koFJFuOW285R3ihdOPofvIucBtItc6UQASLCHLwyoHFyG21jZyq2xzoXxunKAjJK8VotL-SsYfgRZDFIts/s1600/runnable_jar4.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
Ну и, конечно же, никто не запрещает вам добавлять свои собственные цели и задачи в этот Ant-скрипт. Подробнее об Ant&#39;е я писал &lt;a href=&quot;http://eqlbin.blogspot.com/2011/09/apache-ant.html&quot;&gt;тут&lt;/a&gt;.&lt;/div&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;



Ресурсы&lt;/h2&gt;
Кроме скомпилированных классов, в JAR также можно хранить и другие ресурсы приложения, например, текстовые файлы или картинки. Для этого нужно поместить ресурсы в пакет (или субпакет) с каким-нибудь классом и использовать методы &lt;b&gt;Class.getResource(String name)&lt;/b&gt; или &lt;b&gt;Class.getResourceAsStream(String name)&lt;/b&gt; для доступа к ресурсу. Параметр &lt;b&gt;name&lt;/b&gt; это имя файла ресурса относительно каталога (пакета), в котором лежит класс.&lt;br /&gt;
&lt;h4 style=&quot;text-align: left;&quot;&gt;




Пример&lt;/h4&gt;
У вас есть класс &lt;b&gt;MyApp.class&lt;/b&gt;, который находится в пакете &lt;b&gt;my.application&lt;/b&gt;. Вы можете поместить файлы изображений например в &lt;b&gt;my.application.images&lt;/b&gt; и обращаться к ним из класса MyApp примерно так:&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;URL url = getClass().getResource(&quot;images/my_picture.png&quot;);        
Image image = Toolkit.getDefaultToolkit().getImage(url);
&lt;/pre&gt;
Или из любого другого класса:
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;URL url = MyApp.class.getResource(&quot;images/my_picture.png&quot;);        
Image image = Toolkit.getDefaultToolkit().getImage(url);
&lt;/pre&gt;
&lt;br /&gt;
При использовании такого подхода, можно получать доступ как к незапакованным файлам ресурсов, так и к тем, которые запакованы в JAR вместе с классами.

&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/2849817689790275006/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2012/04/runnable-jar-eclipse.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/2849817689790275006'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/2849817689790275006'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2012/04/runnable-jar-eclipse.html' title='Автоматическое создание Runnable JAR в Eclipse и ресурсы'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZBAUcx3MkK4txoamNP-ZkYk6lxrA0aJzKBwne2HMMYEIxgoHXjdQqfRUNv5Ao_IO2JlshbDvEWBuhRVrVBmLKMJcCkQ-BPFCyg40nle9pkpzdAKUU76b8IZSEtacTZlSZBiThyphenhyphenYWJFwoa/s72-c/runnable_jar1.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-3989260417450074929</id><published>2012-04-11T06:17:00.000+04:00</published><updated>2012-04-13T03:43:51.777+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="CMS"/><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="Magnolia"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><title type='text'>Установка Magnolia Community Edition из WAR-файла</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&lt;a href=&quot;http://www.magnolia-cms.com/&quot;&gt;Magnolia Community Edition&lt;/a&gt; &lt;/span&gt;на первый взгляд очень достойная современная WCMS с открытым исходным кодом. Для её познания есть довольно неплохая &lt;a href=&quot;http://documentation.magnolia-cms.com/&quot;&gt;документация&lt;/a&gt; и &lt;a href=&quot;http://wiki.magnolia-cms.com/&quot;&gt;wiki&lt;/a&gt;. Однако, есть у меня подозрения, что эта штука не относится к числу простых и легкоподнимаемых CMS. Но выглядит, повторюсь, очень достойно. Пока что просто установим её под Linux.&lt;/div&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;




&lt;span style=&quot;font-size: large;&quot;&gt;Установка Magnolia CMS &lt;/span&gt;&lt;/h2&gt;
1. Скачать и распаковать &lt;a href=&quot;http://tomcat.apache.org/&quot;&gt;Apache Tomcat&lt;/a&gt; в какой-либо каталог, который далее будет обозначаться [tomcat]&lt;/div&gt;
2. В каталоге [tomcat]/bin создать исполняемый файл setenv.sh, со строкой &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;export CATALINA_OPTS=&quot;$CATALINA_OPTS -XX:MaxPermSize=256m -Xms128M -Xmx768M -Djava.awt.headless=true&quot;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
Файл setenv.sh будет автоматически выполнен при запуске tomcat и JVM будет запущена с аргументами, которые указаны в файле. Кратко о каждом:&lt;/div&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;-Xms128M и -Xmx768M устанавливают, соответственно, минимальный и максимальный размер кучи. Параметр -Xms128M не особо обязательный, если речь не идёт о повышении производительности, а вот без -Xmx768M установка Magnolia скорее всего будет падать с &lt;b&gt;java.lang.OutOfMemoryError: Java heapspace&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;-XX:MaxPermSize=256m увеличивает размер  области памяти, которая называется Permanent Generation. Эта память не  относится к куче, в ней хранятся классы, методы и другие полезные вещи.  Если до установки Magnolia не увеличить эту область памяти, то установка  будет валиться с &lt;b&gt;java.lang.OutOfMemoryError: PermGen space&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;-Djava.awt.headless=true пригодится, если Magnolia будет запускаться на сервер, на котором не запущены Иксы.&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
После завершения установки, выделяемую память, вероятно, можно немного уменьшить, но это надо проверять.&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
3. Скачать WAR-файл с Magnolia. На данный момент последний доступный это &lt;b&gt;magnolia-bundled-webapp-4.5.2.war&lt;/b&gt;. &lt;br /&gt;
&lt;br /&gt;
4. В каталог [tomcat]/webapps положить две копии этого файла под именами &lt;b&gt;magnoliaAuthor.war&lt;/b&gt; и &lt;b&gt;magnoliaPublic.war&lt;/b&gt;.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
При запуске tomcat magnoliaAuthor будет автоматически сконфигурирована как приватная авторская часть CMS, а magnoliaPublic как публичная. Авторская часть, как и в любых других CMS, используется для подготовки публикаций, которые могут создаваться постепенно и во время создания быть недоступны публично. При этом сам автор может просматривать то, что у него получается в ходе работы над контентом. Как только работа над контентом завершена, автор публикует его и контент становится доступным через публичную часть CMS.&lt;/blockquote&gt;
5. Запустить Tomcat ([tomcat]/bin/startup.sh) и подождать пока распакуются оба WAR&#39;а.&lt;br /&gt;
&lt;br /&gt;
6. В браузере сначала постучаться по адресу http://localhost:8080/magnoliaAuthor, нажать &quot;Start install&quot;, дождаться завершения и нажать &quot;Start up Magnolia&quot;. Потом тоже самое проделать для http://localhost:8080/magnoliaPublic.&lt;br /&gt;
&lt;br /&gt;
На этом установка завершена. Для доступа к http://localhost:8080/magnoliaAuthor введите superuser в качестве логина и пароля.По адресу http://localhost:8080/magnoliaPublic по умолчанию будет доступен демонстрационный сайт, над которым можно поиздеваться через magnoliaAuthor.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinOFNknkMvoeQaUZ4XKQrzsSOaPE4_iynN7_QG91Ddn9PF8c2yXQDZVwUDQmpKDVSE_Y8lZInwACz-gGkjaqW5dMy-ii8xFiUssV3UwOygpD37Kk1Dq9vqR5DMIU5-HjWdJlzLUXqwjehV/s1600/magnolia1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;387&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinOFNknkMvoeQaUZ4XKQrzsSOaPE4_iynN7_QG91Ddn9PF8c2yXQDZVwUDQmpKDVSE_Y8lZInwACz-gGkjaqW5dMy-ii8xFiUssV3UwOygpD37Kk1Dq9vqR5DMIU5-HjWdJlzLUXqwjehV/s640/magnolia1.png&quot; width=&quot;640&quot; /&gt;&amp;nbsp;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYvkSmAA8fo4ofLWkVDhOwckHHFS29EtUgg3-VLdFTGjr7SeEWGPJZR0rdSeH6zAVVYbZmzQg9oAxq0qTX8HagjbgvHHvBEwKLhe1tV9zBYJeOUMW5aQmzxNIStuKZrda84F0n_u3nNIxU/s1600/magnolia2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;201&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYvkSmAA8fo4ofLWkVDhOwckHHFS29EtUgg3-VLdFTGjr7SeEWGPJZR0rdSeH6zAVVYbZmzQg9oAxq0qTX8HagjbgvHHvBEwKLhe1tV9zBYJeOUMW5aQmzxNIStuKZrda84F0n_u3nNIxU/s640/magnolia2.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;




&lt;span style=&quot;font-size: large;&quot;&gt;Возможные проблемы&lt;/span&gt;&lt;/h2&gt;
Если установка или работа Magnolia CMS вдет себя странно, то гляньте в логи Tomcat:&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Если увидите в логах &lt;b&gt;java.lang.OutOfMemoryError&lt;/b&gt;, то попробуйте ещё увеличить&lt;b&gt; &lt;/b&gt;размеры Heap или Permanent Generation.&lt;/li&gt;
&lt;li&gt;Если обнаружите исключения с припиской &quot;Too many open files&quot;, то нужно увеличить лимит на количество открытых файлов в Linux&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;margin-bottom: 0cm;&quot;&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
Общее количество файлов&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Установить в файле /etc/sysctl.conf параметр &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;fs.file-max = 400000&lt;/span&gt;&lt;/b&gt;, и для обновления параметра в системе выполняем &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;sysctl -p&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Проверить можно так: &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;sysctl -a |grep fs.file-max&lt;/span&gt; &lt;/li&gt;
&lt;/ul&gt;
Количество файлов на пользователя&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;В файле /etc/security/limits.conf написать &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;eqlbin hard nofile 50000&lt;/span&gt;&lt;/b&gt; и &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;eqlbin soft nofile 10000&lt;/span&gt;&lt;/b&gt;, только вместо eqlbin, естественно, ваш пользователь, ну или символ * для любого пользователя&lt;/li&gt;
&lt;li&gt;Файл limits.conf читается при загрузке. Чтобы сразу установить новое значение лимита выполните: &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;ulimit -n 50000&lt;/span&gt; из под того пользователя, под которым запускается Tomcat. Если эта операция запрещена в вашем дистрибутиве для обычного пользователя, то проще установить значение в limits.conf и перезагрузиться&lt;/li&gt;
&lt;li&gt;Проверить: &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;ulimit -n&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/3989260417450074929/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2012/04/magnolia-community-edition-war.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/3989260417450074929'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/3989260417450074929'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2012/04/magnolia-community-edition-war.html' title='Установка Magnolia Community Edition из WAR-файла'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinOFNknkMvoeQaUZ4XKQrzsSOaPE4_iynN7_QG91Ddn9PF8c2yXQDZVwUDQmpKDVSE_Y8lZInwACz-gGkjaqW5dMy-ii8xFiUssV3UwOygpD37Kk1Dq9vqR5DMIU5-HjWdJlzLUXqwjehV/s72-c/magnolia1.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-671184718208142315</id><published>2012-04-05T10:19:00.001+04:00</published><updated>2012-04-13T03:39:50.708+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="CMS"/><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><category scheme="http://www.blogger.com/atom/ns#" term="Программирование"/><title type='text'>Список CMS на Java</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Давно хотел найти для себя какую-нибудь удобную CMS, а точнее WCMS, которая:&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Создана на стеке Java-технологий&lt;/li&gt;
&lt;li&gt;Open Source&lt;/li&gt;
&lt;li&gt;Легко разворачивается&lt;/li&gt;
&lt;li&gt;Имеет развитую и относительно простую систему шаблонов&lt;/li&gt;
&lt;li&gt;Более или менее легко интегрируется с другими веб-приложениями&lt;/li&gt;
&lt;li&gt;Проста в освоении для &quot;нетехнических&quot; людей, которые будут наполнять контент&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
Понятное дело, что ничего идеального не бывает, поэтому просто хочется найти компромиссное решение. Это будет непросто, т.к. я никогда ни с какими CMS не общался достаточно тесно  и знаю о них не особо много.&lt;br /&gt;
&lt;br /&gt;
В качестве первого шага на пути к поиску наиболее подходящего кандидата, я поспрашивал у могучего Интернета, какие вообще есть открытые CMS, на Джаве писанные. Оказалось, что как и любых других CMS, решений на базе Java уже вагон и маленькая тележка, причем открытых. Этот список найденных CMS я тут и оставлю. В первую очередь это, конечно, для себя любимого, но может он будет полезен и ещё кому-нибудь.&lt;br /&gt;
&lt;br /&gt;
Список я разделил на две части. В первом списке главные кандидаты. Это те CMS, с которыми я уже успел немного познакомиться: какие-то разворачивал и тыкал ранее, а какие-то просто внимательно изучал на официальных сайтах и они мне на первый взгляд понравились. Во втором списке системы, в которых я не особо уверен, потому что пока не уделил им достаточно внимания. Возможно, с течением времени эти списки будут пополняться или между ними будут происходить перестановки.&lt;br /&gt;
&lt;br /&gt;
Также, вероятно, я буду периодически писать в блог различные заметки об опробованных CMS, а на этой странице повтыкаю ссылки на эти заметки.&lt;br /&gt;
&lt;br /&gt;
Итак, списки.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Главные претенденты:&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;OpenCMS (&lt;a href=&quot;http://www.opencms.org/&quot;&gt;www.opencms.org&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://eqlbin.blogspot.com/search/label/Magnolia&quot;&gt;Magnolia&lt;/a&gt; (&lt;a href=&quot;http://www.magnolia-cms.com/&quot;&gt;www.magnolia-cms.com&lt;/a&gt;)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Alfresco (&lt;a href=&quot;http://www.alfresco.com/&quot;&gt;www.alfresco.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Jahia (&lt;a href=&quot;http://www.jahia.com/&quot;&gt;www.jahia.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Nuxeo (&lt;a href=&quot;http://www.nuxeo.com/&quot;&gt;www.nuxeo.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Liferay (&lt;a href=&quot;http://www.liferay.com/&quot;&gt;www.liferay.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;OpenWGA (&lt;a href=&quot;http://www.openwga.com/&quot;&gt;www.openwga.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;dotCMS (&lt;a href=&quot;http://dotcms.com/&quot;&gt;dotcms.com&lt;/a&gt;) &lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;b&gt;Запасной состав:&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Hippo (&lt;a href=&quot;http://www.onehippo.org/&quot;&gt;www.onehippo.org&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Daisy (&lt;a href=&quot;http://www.daisycms.org/&quot;&gt;www.daisycms.org&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Vosao (&lt;a href=&quot;http://www.vosao.org/&quot;&gt;www.vosao.org&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;eXo Platform (&lt;a href=&quot;http://www.exoplatform.com/&quot;&gt;www.exoplatform.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;InfoGlue (&lt;a href=&quot;http://www.infoglue.org/&quot;&gt;www.infoglue.org&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Enonic (&lt;a href=&quot;http://www.enonic.com/&quot;&gt;www.enonic.com&lt;/a&gt;) &lt;/li&gt;
&lt;li&gt;JadaSite (&lt;a href=&quot;http://www.jadasite.com/&quot;&gt;www.jadasite.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fedora Commons (&lt;a href=&quot;http://fedora-commons.org/&quot;&gt;fedora-commons.org&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;LogicalDOC (&lt;a href=&quot;http://www.logicaldoc.com/&quot;&gt;www.logicaldoc.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;DSpace (&lt;a href=&quot;http://www.dspace.org/&quot;&gt;www.dspace.org&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;OpenKM (&lt;a href=&quot;http://www.openkm.com/&quot;&gt;www.openkm.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Yanel (&lt;a href=&quot;http://yanel.org/&quot;&gt;yanel.org&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
Многие CMS из этих списков имеют версии &lt;span lang=&quot;en&quot;&gt;Enterprise и Community, которые, соответственно, распространяются под разными лицензиями: закрытой и открытой. Также, некоторые кандидаты являются не только WCMS, а большими разухабистыми ECMS (как, например, Alfresco) с возможностями WCMS. А вот в запасной список наоборот могли закрасться те, которые вообще не умеют WCMS, а являются к примеру только &lt;a href=&quot;http://ru.wikipedia.org/wiki/Document_management_system&quot;&gt;DMS&lt;/a&gt;.&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/671184718208142315/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2012/04/cms-java.html#comment-form' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/671184718208142315'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/671184718208142315'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2012/04/cms-java.html' title='Список CMS на Java'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-3475874270123437676</id><published>2012-03-14T04:40:00.002+04:00</published><updated>2012-03-14T04:42:38.300+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="d-link"/><category scheme="http://www.blogger.com/atom/ns#" term="Wi-Fi"/><category scheme="http://www.blogger.com/atom/ns#" term="гадость"/><title type='text'>Точка доступа D-Link DAP-1150 RU которая не тонет</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;&quot;Посчастливилось&quot; мне тут повозиться с этим редкостным г... ну тем в общем, что не тонет. Эта гадость в режиме AP Mode раздает по DHCP беспроводным клиентам в качестве шлюза по умолчанию и DNS свой собственный адрес. Естественно, в такой ситуации клиенты этой точки ограничены только работой в своей сети и никаких им интернетов. Вылечить это психическое расстройство удалось только заменой родной прошивки версии 2.0 на &lt;a href=&quot;ftp://ftp.dlink.ru/pub/Wireless/DAP-1150_RU/Firmware/Rev%20B%202.11_beta/dap1150_v2.11_ba9i.bin&quot;&gt;вот эту 2.11 beta&lt;/a&gt;. Хоть она и beta, но с ней DHCP заработал адекватно и кроме этого появился режим Wireless Client Mode. Сначала пробовал прошивать &lt;a href=&quot;ftp://ftp.dlink.ru/pub/Wireless/DAP-1150_RU/Firmware/Rev%20B%202.10/dap1150_v2.10.bin&quot;&gt;стабильной версией 2.10&lt;/a&gt; (стабильной по мнению разработчиков). Прошилось легко, но толку никакого. Так что, товарищи, если вам &quot;повезло&quot; как мне, то рекомендую воспользоваться ссылочкой выше.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/3475874270123437676/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2012/03/d-link-dap-1150-ru.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/3475874270123437676'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/3475874270123437676'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2012/03/d-link-dap-1150-ru.html' title='Точка доступа D-Link DAP-1150 RU которая не тонет'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-5395286927341122686</id><published>2011-10-11T14:29:00.001+04:00</published><updated>2011-10-12T01:07:08.502+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AppEngine"/><category scheme="http://www.blogger.com/atom/ns#" term="Eclipse"/><category scheme="http://www.blogger.com/atom/ns#" term="Google"/><category scheme="http://www.blogger.com/atom/ns#" term="GWT"/><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="Программирование"/><title type='text'>Плагин Google для Eclipse: обновления о которых не просят</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;Обнаружил интересную особенность у плагина Google для Eclipse, позволяющего разрабатывать приложения с использованием GWT и App Engine. &lt;br /&gt;
&lt;br /&gt;
После очередного перезапуска контейнера сервлетов Jetty, который используется плагином для запуска GWT приложений на локальном хосте, он как-то необычно призадумался на старте. Вывел мне на консоль &quot;Initializing App Engine server&quot; и замолчал. Понимаю, что тужится, но не понимаю с чего это вдруг. Подождал я его несколько минут и он выдавил на консоль что-то вроде этого:&lt;br /&gt;
&lt;blockquote style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;INFO: Unable to access http://appengine.google.com/api/updatecheck?runtime=java&amp;amp;release=1.5.4×tamp=1315604504&amp;amp;api_versions=[&#39;1.0&#39;] &lt;/blockquote&gt;Оказалось, что этот негодяй при каждом старте проверяет наличие обновлений, а у меня тут как раз отвалился интернет. Мне в принципе не жалко, пусть проверяет, но ждать его по несколько минут при каждом перезапуске когда нет сети как-то не особо хочется.&lt;br /&gt;
&lt;br /&gt;
Первым делом я, конечно, полез в настройки Eclipse (Window -&amp;gt; Preferences -&amp;gt; Google) и убрал галку с пункта &quot;Notify me about updates&quot;, но ничего не изменилось. Немного пошарив по интернету, я наткнулся на забавное описание метода &lt;a href=&quot;http://www.jarvana.com/jarvana/view/com/google/appengine/appengine-java-sdk/1.5.3/appengine-java-sdk-1.5.3.zip%21/appengine-java-sdk-1.5.3/docs/tools/javadoc/index.html&quot;&gt;allowedToCheckForUpdates()&lt;/a&gt; класса UpdateCheck в пакете &lt;b&gt;com.google.appengine.tools.info&lt;/b&gt;. По названию пакета можно сделать некоторые выводы о том, для чего он нужен. Вот описание этого метода:&lt;br /&gt;
&lt;blockquote&gt;&lt;i&gt;Returns true if the user wants to check for updates even when we don&#39;t need to. We assume that users will want this functionality, but they can opt out by creating an .appcfg_no_nag file in their home directory. &lt;/i&gt;&lt;/blockquote&gt;В общем, надо или не надо, этот метод все равно возвращает true, т.к. разработчики уверены что, если пользователь даже отключил проверку обновлений, то он всё равно хочет обновлений. Как-то так получается :) Однако, всё-таки можно эту проверку отключить, создав в домашнем каталоге пользователя, под которым запускается контейнер, файл &lt;b&gt;.appcfg_no_nag&lt;/b&gt;. После того, как я это сделал, &quot;задумчивость&quot; контейнера на старте прошла, но tcpdump показывает, что он всё равно продолжает стучаться на appengine.google.com.&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/5395286927341122686/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2011/10/google-eclipse.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/5395286927341122686'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/5395286927341122686'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2011/10/google-eclipse.html' title='Плагин Google для Eclipse: обновления о которых не просят'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-5322004759700831823</id><published>2011-09-29T06:19:00.000+04:00</published><updated>2014-10-08T13:36:09.476+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Ant"/><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="Программирование"/><title type='text'>Основы использования системы сборки Apache Ant</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;Когда ведется разработка программного продукта, он может состоять из множества исходных частей. Например, разработка может вестись несколькими людьми практически независимо друг от друга, а файлы с исходным кодом, за который они отвечают, могут находится в совершенно разных местах файловой системы и даже на разных машинах. Кроме файлов исходного кода, в проекте могут использоваться также и готовые библиотеки от сторонних разработчиков (например, файлы jar, dll или so), файлы конфигурации, различные картинки и иконки, звуковые файлы и куча всего остального. Все эти штуки могут так же изменяться в ходе разработки проекта другими людьми. &lt;br /&gt;
&lt;br /&gt;
А теперь представьте, что из всего этого безобразия необходимо собрать всего один файл сжатого архива, который пользователь мог бы скачать с вашего сайта, распакавать и сразу же запустить ваш продукт. Вам нужно будет собрать вместе все файлы с исходным кодом Java и скомпилировать их, прогнать тесты, чтобы убедиться что проект никто не поломал, полученные class-файлы упаковать в JAR (или несколько JAR&#39;ов), для которого ещё необходимо сгенерировать правильный манифест, содержащий CLASSPATH, имя главного класса, номер версии и т.п., затем собрать вместе все остальные файлы вроде библиотек, картинок и конфигов, распихать это все в правильные каталоги, сгенерировать документацию, создать из всего этого архив и залить его на ваш вэб-сервер, а всё произошедшее записать в лог и сохранить его в базе данных, которая хранит информацию о различных сборках. Делать всё это руками довольно уныло, да и не должен психически здоровый человек этим заниматься. &lt;br /&gt;
&lt;br /&gt;
Системы сборки, такие как &lt;a href=&quot;http://ant.apache.org/&quot;&gt;Apache Ant&lt;/a&gt;, были созданы для того, чтобы автоматизировать весь этот порой достаточно сложный и рутиный процесс по сборке проекта. Причем, в случае с Apache Ant проект не обязательно должен быть на Java, и не обязательно должен быть вообще программным обеспечением. Это может быть что угодно, хотя ориентирован он, конечно же, в первую очередь на Java. В данной статье я хочу на очень простом примере показать, как можно облегчить себе жизнь с помощью Apache Ant.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: x-large;&quot;&gt;Минимальные знания об использовании Apache Ant&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
О том, как собирать проект, ant узнает из специального xml-файла, в котором разработчик описывает все шаги, необходимые для сборки. Обычно этот файл носит имя &lt;b&gt;build.xml&lt;/b&gt;, но оно может быть и другим. Просто при запуске ant сам ищет файл build.xml в каталоге из которого он запущен, а при использовании нестандартного имени необходимо передать его программе ant в качестве параметра.&lt;br /&gt;
&lt;br /&gt;
Файл build.xml содержит внутри себя корневой элемент project, наполнением которого являются элементы, относящиеся к одному из следующих понятий:&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Цели (Targets)&lt;/li&gt;
&lt;li&gt;Задачи (Tasks)&lt;/li&gt;
&lt;li&gt;Ресурсы (Resources)&lt;/li&gt;
&lt;li&gt;Свойства (Properties)&lt;/li&gt;
&lt;/ul&gt;&lt;b&gt;Задачи&lt;/b&gt; представляют из себя различные атомарные операции: компиляция исходных файлов Java, создание JAR, создание каталога, копирование файлов, вывод информации на экран и т.п.. Задачи могут использовать для своей работы отдельные &lt;b&gt;ресурсы&lt;/b&gt; (например, файл) или &lt;b&gt;коллекцию ресурсов&lt;/b&gt; (множество файлов). &lt;b&gt;Цели&lt;/b&gt;, в свою очередь, состоят из одной или нескольких задач, поэтому цель можно рассматривать как функцию или процедуру в языках программирования. Целями могут быть: создание дерева каталогов, компиляция всего проекта, создание дистрибутива, удаление результатов предыдущей компиляции, и всё в таком духе.&lt;br /&gt;
&lt;br /&gt;
Если термины &quot;Цель&quot; и &quot;Задача&quot; запутывают вас, то попробуйте размышлять как-нибудь так: &quot;Для того чтобы достичь какую-либо цель, нужно выполнить определенные задачи&quot;. Например, чтобы достичь цель &quot;Скомпилировать проект&quot;, необходимо выполнить задачи: &quot;Создать каталог для скомпилированных файлов&quot;, &quot;Запустить компилятор javac&quot; и &quot;Создать JAR&quot;.&lt;br /&gt;
&lt;br /&gt;
Кроме целей, задач и ресурсов есть &lt;b&gt;свойства&lt;/b&gt;. К свойствам можно относится как к переменным, а точнее константам. У свойства есть имя и значение в виде строки. Значение свойства устанавливается один раз и любая попытка изменить это значение игнорируется. Свойства обычно используются в атрибутах задач. Установка значения может происходить в самом файле build.xml или во внешнем properties-файле, что очень удобно и позволяет менять процесс сборки не изменяя build.xml.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Свойства&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
В файле build.xml значение свойства может быть установлено с помощью элемента property. Этот элемент может использоваться как внутри корневого элемента project, так и внутри target&#39;ов:&lt;br /&gt;
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;property name=&quot;myProp&quot; value=&quot;myValue&quot;/&gt;;
]]&gt;
&lt;/script&gt;&lt;br /&gt;
Как уже было сказано, свойства могут быть определены и во внешнем файле properties. Записи свойств в этом файле могут выглядеть так:&lt;br /&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;myProp=myValue
my.text = Very useful text
&lt;/pre&gt;Чтобы свойства, записанные в файле, стали доступны в сценарии сборки, нужно его подключить:&lt;br /&gt;
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;property file=&quot;build.properties&quot;/&gt;
]]&gt;
&lt;/script&gt;&lt;br /&gt;
Для получения доступа к значению свойства используется конструкция ${myProp}. Следующий код выводит на экран значение свойства с именем myProp:&lt;br /&gt;
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;echo&gt;${myProp}&lt;/echo&gt;
]]&gt;
&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Ресурсы&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Примером ресурсов могут быть, например, элементы file и url:&lt;br /&gt;
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;file file=&quot;/home/user/files/somefile.txt&quot;/&gt;
&lt;url url=&quot;http://ant.apache.org/index.html&quot;/&gt;
]]&gt;
&lt;/script&gt;&lt;br /&gt;
Думаю, что тут всё понятно без лишних слов.&lt;br /&gt;
&lt;br /&gt;
Однако, чаще удобнее пользоваться не отдельными ресурсами, а коллекциями ресурсов - элементами, которые представляют группу схожих ресурсов. Например, элемент fileset представляет группу файлов, которые могут быть описаны множеством разных способов.&lt;br /&gt;
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;fileset dir=&quot;src&quot; casesensitive=&quot;yes&quot;&gt;
  &lt;include name=&quot;**/*.java&quot;/&gt;
  &lt;exclude name=&quot;**/*Test*&quot;/&gt;
&lt;/fileset&gt;
]]&gt;
&lt;/script&gt;&lt;br /&gt;
В этом примере в fileset войдут все файлы java из каталога src и его подкаталогов, кроме файлов, в имени которых есть слово Test с учётом регистра.&lt;br /&gt;
&lt;br /&gt;
Помимо описанных вариантов ресурсов, также существуют, так называемые, path-like структуры. Не знаю, стоит ли относить их к коллекциям ресурсов, но лучше места для них я не придумал, поэтому упомяну эти структуры тут. Из названия, думаю, понятно, что это элементы которые описывают такие вещи как пути (PATH). Их наполнением могут быть различные ресурсы и их коллекции.&lt;br /&gt;
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;path id=&quot;libs&quot;&gt;
  &lt;fileset dir=&quot;${mylib.dir}&quot; includes=&quot;statistic/*.jar&quot; /&gt;
  &lt;fileset dir=&quot;${extlib.dir}&quot;&gt;
    &lt;include name=&quot;commons-math/commons-math-2.2.jar&quot;/&gt;
    &lt;include name=&quot;jfreechart/*.jar&quot;/&gt;
  &lt;/fileset&gt;
  &lt;file file=&quot;/home/user/libs/somelib.jar&quot;/&gt;
&lt;/path&gt;
]]&gt;
&lt;/script&gt;&lt;br /&gt;
Обратите внимание, что в данном примере так же используются свойства mylib.dir и extlib.dir, значением которых являются пути к каталогам с собственными и внешнимим библиотеками, соответственно. &lt;br /&gt;
&lt;br /&gt;
В описанных path-like структурах, Ant будет автоматически использовать правильный символ сепаратора для разделения ресурсов, соответствующий операционной системе, на которой он выполняется. &lt;br /&gt;
&lt;br /&gt;
Очень удобной возможностью в path-like структурах является возможность одной структуры ссылаться на другую, используя атрибуты id и refid:&lt;br /&gt;
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;path id=&quot;libsExtended&quot;&gt;
  &lt;path refid=&quot;libs&quot;/&gt;
  &lt;file file=&quot;/home/user/another_libs/anotherlib.jar&quot;/&gt;
&lt;/path&gt;
]]&gt;
&lt;/script&gt;&lt;br /&gt;
Более подробно о ресурсах можно почитать &lt;a href=&quot;http://ant.apache.org/manual/Types/resources.html&quot;&gt;тут&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Задачи&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Задачи представляют действия, которые могут осуществляться с какими-либо ресурсами или без их участия. Наиболее часто встречающиеся задачи представлены в таблице.&lt;br /&gt;
&lt;style type=&quot;text/css&quot;&gt;
 
table.table { border: black thin solid;}   
table.table td, table.table th { border: black thin solid; padding: 5px;}
table.table th.head { padding: 5px; text-align: center;}
&lt;/style&gt;&lt;br /&gt;
&lt;table cellspacing=&quot;0&quot; class=&quot;table&quot;&gt;&lt;tbody&gt;
&lt;tr&gt; &lt;th class=&quot;head&quot; colspan=&quot;2&quot;&gt;Часто используемые задачи&lt;/th&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;th&gt;Задача (xml-тег)&lt;/th&gt;&lt;th&gt;Действие&lt;/th&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;javac&lt;/td&gt;&lt;td&gt;компиляция файлов с исходным кодом java&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;jar&lt;/td&gt; &lt;td&gt;создание java-архива&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;zip&lt;/td&gt; &lt;td&gt;создание zip-архива&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;mkdir&lt;/td&gt; &lt;td&gt;создание директории&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;copy&lt;/td&gt; &lt;td&gt;копирование файлов&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;delete&lt;/td&gt; &lt;td&gt;удаление файлов и директорий&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;exec&lt;/td&gt; &lt;td&gt;выполнение внешней команды&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;mail&lt;/td&gt; &lt;td&gt;отправка электронной почты&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;junit&lt;/td&gt; &lt;td&gt;запуск unit-тестов&lt;/td&gt; &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
Ниже приведу простейшие примеры использования задач.&lt;br /&gt;
&lt;br /&gt;
Создание каталога:&lt;br /&gt;
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;mkdir dir=&quot;${build.dir}&quot;/&gt;
]]&gt;
&lt;/script&gt;&lt;br /&gt;
Копирование файла:&lt;br /&gt;
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;copy file=&quot;somefile.txt&quot; tofile=&quot;somefilecopy.txt&quot;/&gt;
]]&gt;
&lt;/script&gt;&lt;br /&gt;
Компиляция файлов с кодом java:&lt;br /&gt;
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;javac srcdir=&quot;${src.dir}&quot; destdir=&quot;${build.dir}&quot; classpath=&quot;somelib.jar&quot;/&gt;
]]&gt;
&lt;/script&gt;&lt;br /&gt;
Иногда для выполнения некоторых задач хватает лишь указания правильных атрибутов, но кроме этого внутри тега задачи могут находиться и вложенные элементы, которые, например, могут описывать ресурсы с которыми необходимо работать задаче. Более интересное применение различных задач будет продемонстрированно позже на простом примере сборки проекта.&lt;br /&gt;
&lt;br /&gt;
Полный список задач представлен &lt;a href=&quot;http://ant.apache.org/manual/tasksoverview.html&quot;&gt;здесь&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Цели&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Итак, мы подошли к цели :) На целях строится весь сценарий сборки. Вы ставите цели и достигаете их с помощью выполнения задач и использования ресурсов. Зачем использовать цели, если все задачи можно писать прямо в корневом элементе project? А затем, что цели позволяют разделить сценарий сборки на отдельные законченные логические этапы, которые могут быть зависимыми друг от друга. Т.е. одна цель может быть помечена как зависимая от другой цели, и для её достижения необходимо, чтобы сначала  была достигнута цель от которой она зависит. &lt;br /&gt;
&lt;br /&gt;
В случае, если задачи расположены в корневом элементе project, то ant, проходя по файлу build.xml, последовательно их выполняет. При таком подходе вы можете определить для себя только один вариант выполнения сборки. При использовании целей можно указать ant&#39;у какую цель необходимо достичь, а он уже сам проверит её зависимости и выполнит сначала цели от которых зависит главная.&lt;br /&gt;
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;project name=&quot;ant.dependencies&quot; default=&quot;compile&quot; &gt;

  &lt;target name=&quot;make.build.dirs&quot;&gt;
    &lt;echo&gt;creating directories for build...&lt;/echo&gt;
  &lt;/target&gt;

  &lt;target name=&quot;make.dist.dirs&quot;&gt;
    &lt;echo&gt;creating directories for distribute...&lt;/echo&gt;
  &lt;/target&gt;
  
  &lt;target name=&quot;compile&quot; depends=&quot;make.build.dirs&quot;&gt;
    &lt;echo&gt;compiling...&lt;/echo&gt;
  &lt;/target&gt;
  
  &lt;target name=&quot;dist&quot; depends=&quot;compile, make.dist.dirs&quot;&gt;
    &lt;echo&gt;creating a distribution...&lt;/echo&gt;
  &lt;/target&gt;

  &lt;target name=&quot;clean&quot;&gt;
    &lt;echo&gt;cleaning...&lt;/echo&gt;
  &lt;/target&gt;
  
&lt;/project&gt;
]]&gt;
&lt;/script&gt;&lt;br /&gt;
В приведенном примере описаны вполне реальные цели, только вместо элементов echo в них должны быть более полезные задачи. В элементе project установлено значение атрибута default равное compile, которое означает, что по умолчанию ant будет выполнять цель compile, которая зависит от цели make.build.dirs, поэтому сначала будет выполнена цель make.build.dirs, а затем compile. Если же ant&#39;у при запуске указать цель dist, которая зависит одновременно от двух целей compile и make.dist.dirs, то сначала будет выполнен этап описанный выше, затем make.dist.dirs, а уже потом dist:&lt;br /&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;make.build.dirs:
     [echo] creating directories for build...

compile:
     [echo] compiling...

make.dist.dirs:
     [echo] creating directories for distribute...

dist:
     [echo] creating a distribution...
&lt;/pre&gt;Кроме этого в примере также есть цель clean, которая не зависит ни от какой другой цели, и также нет цели которая зависила бы от clean, поэтому данная цель может быть только явно вызвана ant&#39;ом: ant clean. &lt;br /&gt;
&lt;br /&gt;
В дополнение к зависимостям цели от цели, для управлением процессом сборки существует зависимость цели от свойства, точнее в терминах ant&#39;а это называется условиями. Суть условий заключается в том, что цель может выполняться или не выполняться, в зависимости от того, установлено ли значение какого-либо свойства или нет.&lt;br /&gt;
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;project name=&quot;ant.conditions&quot; default=&quot;compile&quot; &gt;

  &lt;property name=&quot;full&quot; value=&quot;true&quot;/&gt; 

  &lt;target name=&quot;build.simple&quot; unless=&quot;full&quot;&gt;
    &lt;echo&gt;simple building...&lt;/echo&gt;
  &lt;/target&gt;

  &lt;target name=&quot;build.full&quot; if=&quot;full&quot;&gt;
    &lt;echo&gt;full building...&lt;/echo&gt;
  &lt;/target&gt;
  
  &lt;target name=&quot;compile&quot; depends=&quot;build.simple, build.full&quot;&gt;
    &lt;echo&gt;compiling...&lt;/echo&gt;
  &lt;/target&gt;

&lt;/project&gt;
]]&gt;
&lt;/script&gt;&lt;br /&gt;
В данном примере цель build.full будет выполняться только, если установлено значение совйства full, в противном же случае будет выполняться build.simple. Абсолютно неважно какое значение установлено в свойство full, это может быть и true, и false, и любая другая строка, даже пустая.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: x-large;&quot;&gt;Пример сборки проекта с помощью Apache Ant&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Теперь разберем относительно простой процесс сборки маленького проекта. &lt;br /&gt;
&lt;br /&gt;
Для уверенности в том, что сценарий сборки, приведённый ниже, собирает действительно рабочий и самодостаточный проект, я написал очень маленькую программу, код которой я приводить не буду, потому что это в данном случае не нужно. Программа состоит из трёх файлов java: ValuesGenerator.java, Statistic.java и Main.java. В ValuesGenerator.java описан метод для генерации случайного набора данных, в Statistic.java методы для нахождения минимального, среднего и максимального значений массива, а в Main.java находится метод main(), в котором происходит вызов методов классов ValuesGenerator и Statistic. Класс Statistic использует для рассчета значений библиотеку Apache Commons Math, а вся работа программы пишется в лог-файл с помощью библиотеки log4j. Также в каталоге с проектом есть небольшой файл readme.txt с описанием программы. &lt;br /&gt;
&lt;br /&gt;
Задача заключается в том, чтобы иметь возможность автоматически создавать zip-файл с дистрибутивом программы. &lt;br /&gt;
&lt;br /&gt;
Сразу привожу содержание файла свойств и файла build.xml c подробными комментариями, а потом опишу некоторые моменты подробно.&lt;br /&gt;
&lt;br /&gt;
Файл build.properties&lt;br /&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;# имя каталога с дополнительными библиотеками
lib.dir=lib
# каталог проекта
project.dir=project
# каталог с исходниками проекта
project.src.dir=${project.dir}/src
# каталог для сборки
build.dir=build
# имя для jar-файла
build.jar.name=myprogram.jar
# имя главного класса
build.jar.mainclass=eqlbin.tutorials.ant.Main
# имя проекта
dist.name=myprogram
# каталог для создания дистрибутива
dist.dir=distribution
# имя каталога с библиотеками в дистрибутиве
dist.lib.dir=lib
&lt;/pre&gt;&lt;br /&gt;
Файл build.xml&lt;br /&gt;
&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;project name=&quot;ant.tutorial&quot; default=&quot;compile&quot; &gt;
  &lt;!-- Подключение файла свойств --&gt;
  &lt;property file=&quot;build.properties&quot;/&gt;           

  &lt;!-- Создание каталогов для скомпилированных файлов --&gt;
  &lt;target name=&quot;make.build.dirs&quot;&gt;
    &lt;mkdir dir=&quot;${build.dir}&quot;/&gt;
    &lt;mkdir dir=&quot;${build.dir}/class&quot;/&gt;
  &lt;/target&gt;

  &lt;!-- Компиляция --&gt;
  &lt;target name=&quot;compile&quot; description=&quot;compile java&quot; depends=&quot;make.build.dirs&quot;&gt;
  
    &lt;!-- Подготовка CLASSPATH для проведения компиляции --&gt;
    &lt;path id=&quot;classpath&quot;&gt;
      &lt;!-- Включить в CLASSPATH все jar-файлы из каталогов
      ${lib.dir}/commons-math и ${lib.dir}/log4j, исключив
      те, которые содержат документацию и исходники --&gt;
      &lt;fileset dir=&quot;${lib.dir}&quot; 
               includes=&quot;commons-math/*.jar, log4j/*.jar&quot; 
               excludes=&quot;**/*javadoc*.jar,**/*sources*.jar&quot;
      /&gt;
    &lt;/path&gt;
  
    &lt;!-- Сохранение текстового представления CLASSPATH в свойство --&gt;
    &lt;pathconvert property=&quot;compile.classpath&quot; refid=&quot;classpath&quot;/&gt; 
    &lt;!-- Вывод значения CLASSPATH на экран --&gt;
    &lt;echo&gt;CLASSPATH: ${compile.classpath}&lt;/echo&gt;

    &lt;!-- Запуск компилятора с указанием CLASSPATH и каталога, 
    содержащего файлы исходного кода Java --&gt;
    &lt;javac destdir=&quot;${build.dir}/class&quot;&gt;
      &lt;src path=&quot;${project.src.dir}&quot;/&gt;
      &lt;classpath refid=&quot;classpath&quot;/&gt;
    &lt;/javac&gt;
  &lt;/target&gt;

  &lt;!-- Создание каталогов для дистрибутива --&gt;
  &lt;target name=&quot;make.dist.dirs&quot; description=&quot;Make dirs for distribution&quot;&gt;
    &lt;mkdir dir=&quot;${dist.dir}/${dist.name}&quot;/&gt;
    &lt;mkdir dir=&quot;${dist.dir}/${dist.name}/${dist.lib.dir}&quot;/&gt;
  &lt;/target&gt;
  
  &lt;!-- Копирование всех jar&#39;ов, описанных в CLASSPATH в каталог библиотек 
  дистрибутива, т.к. они необходимы для выполнения программы --&gt;
  &lt;target name=&quot;dist.lib.copy&quot; depends=&quot;make.dist.dirs&quot;&gt;
    &lt;copy todir=&quot;${dist.dir}/${dist.name}/${dist.lib.dir}&quot; flatten=&quot;true&quot;&gt;
      &lt;path refid=&quot;classpath&quot;/&gt;
    &lt;/copy&gt;
  &lt;/target&gt;
  
  &lt;!-- Создание выполняемого java-архива --&gt;
  &lt;target name=&quot;make.jar&quot; description=&quot;Make jar file&quot; depends=&quot;compile,dist.lib.copy&quot;&gt;
          
    &lt;!-- Конвертирование CLASSPATH, созданного для компиляции 
    в CLASSPATH для выполнения java-архива и сохранение в свойство. 
    В качестве сепаратора библиотек устанавливается пробел, т.к. 
    именно он необходим в манифесте --&gt;  
    &lt;pathconvert property=&quot;dist.classpath&quot; pathsep=&quot; &quot; refid=&quot;classpath&quot;&gt;
      &lt;mapper&gt;
        &lt;chainedmapper&gt;
          
          &lt;!-- Удаление информации о пути к jar-файлам: 
          остается только имя файла --&gt;
          &lt;flattenmapper /&gt;
          
          &lt;!-- Добавление перед именем файла имени каталога с 
          библиотеками для дистрибутива --&gt;
          &lt;globmapper from=&quot;*&quot; to=&quot;${dist.lib.dir}/*&quot; /&gt;
        
        &lt;/chainedmapper&gt;
      &lt;/mapper&gt;
    &lt;/pathconvert&gt;        
    
    &lt;!-- Вывод значения CLASSPATH для jar&#39;а на экран --&gt;
    &lt;echo&gt;Class-Path: ${dist.classpath}&lt;/echo&gt;     
    
    &lt;!-- Создание jar --&gt;
    &lt;jar destfile=&quot;${dist.dir}/${dist.name}/${build.jar.name}&quot;&gt;                                  
      &lt;!-- Создание файла MANIFEST.MF --&gt;
      &lt;manifest&gt;
        &lt;attribute name=&quot;Built-By&quot; value=&quot;eqlbin&quot;/&gt;
        &lt;attribute name=&quot;Main-Class&quot; value=&quot;${build.jar.mainclass}&quot;/&gt;
        &lt;attribute name=&quot;Class-Path&quot; value=&quot;${dist.classpath}&quot;/&gt;
      &lt;/manifest&gt;
        
      &lt;!-- Скомпилированные файлы --&gt;
      &lt;fileset dir=&quot;${build.dir}/class&quot;&gt;
        &lt;include name=&quot;**/*.class&quot;/&gt;
      &lt;/fileset&gt;       
    &lt;/jar&gt;      
  &lt;/target&gt;
  
  &lt;!-- Создание документации javadoc--&gt;
  &lt;target name=&quot;javadoc&quot;&gt;        
    &lt;javadoc sourcepath=&quot;${project.src.dir}&quot;
             defaultexcludes=&quot;yes&quot;
             destdir=&quot;${dist.dir}/${dist.name}/api&quot;
             author=&quot;true&quot;
             version=&quot;true&quot;
             use=&quot;true&quot;
             windowtitle=&quot;My Program API&quot;
             charset=&quot;UTF-8&quot;&gt;
           
      &lt;doctitle&gt;&lt;![CDATA[&lt;h1&gt;

My Program API&lt;/h1&gt;]]&gt;&lt;/doctitle&gt;
      &lt;bottom&gt;&lt;![CDATA[&lt;i&gt;Copyright &amp;#169; 2011 eqlbin. All Rights Reserved.&lt;/i&gt;]]&gt;&lt;/bottom&gt;
    &lt;/javadoc&gt; 
  &lt;/target&gt;  
  
  &lt;!-- Создание дистрибутива для распространения --&gt;
  &lt;target name=&quot;dist&quot; description=&quot;Creating a distribution&quot; depends=&quot;make.jar, dist.lib.copy, javadoc&quot;&gt;
    &lt;!-- Копирование файла с полезной информацией в каталог с программой --&gt;
    &lt;copy file=&quot;${project.dir}/readme.txt&quot; todir=&quot;${dist.dir}/${dist.name}&quot;/&gt;
    &lt;!-- Удаление предыдущего архива дистрибутива, если он есть --&gt;
    &lt;delete file=&quot;${dist.dir}/${dist.name}-v1.0.zip&quot;/&gt;
    &lt;!-- Создание zip-архива с исполняемым jar-файлом программы и каталогом с библиотеками --&gt;
    &lt;zip destfile=&quot;${dist.dir}/${dist.name}-v1.0.zip&quot; basedir=&quot;${dist.dir}&quot;/&gt;
  &lt;/target&gt;

  &lt;!-- Удаление результатов предыдущих сборок --&gt;
  &lt;target name=&quot;clean&quot;&gt;
    &lt;delete dir=&quot;${build.dir}&quot;/&gt;
    &lt;delete dir=&quot;${dist.dir}&quot;/&gt;
  &lt;/target&gt;
&lt;/project&gt;
&lt;/script&gt;&lt;br /&gt;
На первый взгляд листинг build.xml может показаться сложным и громоздким. На самом деле, если потратить немного времени и внимательно просмотреть весь код, почитав комментарии, всё должно стать понятно. Ну а громоздкое оно, потому что XML, от этого никуда не деться. Рассмотрим теперь поподробнее приведённый пример. &lt;br /&gt;
&lt;br /&gt;
Цели make.build.dirs и make.dist.dirs вам должны быть уже предельно понятны. Они не делают ничего умного, а только создают каталоги. Единственное, что можно отметить, в данном случае совсем не обязательно было прописывать в них по две задачи mkdir, а можно было бы обойтись одной, т.к. mkdir может создавать целую цепочку каталогов сразу.&lt;br /&gt;
&lt;br /&gt;
Цель compile содержит задания, необходимые для компиляции исходного кода. Перед вызовом компилятора происходит создание элемента path, в котором будут содержаться пути к файлам библиотек, используемых в нашей программе. Файлы определяются с помощью элемента fileset, помимо которого в path могли бы содержаться и другие элементы коллекции ресурсов, например, dirset, содержащий пути к каталогам со скомпилированными java-классами или другой элемент path. Т.к. созданный элемент path представляет из себя необходимый для компиляции CLASSPATH, ему устанавливается соответствующий атрибут id, для того чтобы позже можно было на это path сослаться. После создания path, его текстовое представление записывается в свойство с помощью элемента pathconvert и выводится на экран. Цель compile указана в элементе project, как цель по умолчанию, поэтому если ant&#39;у не указывать цель для выполнения, он будет выполнять именно её. Данная цель зависит от выполнения только одной цели make.build.dirs. &lt;br /&gt;
&lt;br /&gt;
Цель dist.lib.copy тоже простая и осуществляет копирование всех jar-файлов, необходимых для запуска программы в каталог дистрибутива. В качестве источника файлов используется созданный ранее path. Атрибут flatten=&quot;true&quot; в элементе copy отсекает всю информацию о пути к копируемым файлам и оставляет только их имя. Если его не использовать или указать равным false, то при копировании ant будет создавать всё дерево каталогов в каталоге todir, а т.к. в path у нас используются абсолютные пути, которые начинаются с корня (т.е. с символа &quot;/&quot;), то копирование вообще не выполнется. Это важный момент, который иногда является источником ошибок. &lt;br /&gt;
&lt;br /&gt;
Цель make.jar занимается упаковыванием всех скомпилированных java-классов в исполняемый JAR. Для того чтобы создать првильно выполняющийся JAR, необходимо в первую очередь наличие файлов скомпилированных классов и система каталогов в которой также должны находиться файлы используемых программой библиотек. По этой причине цель make.jar зависит от целей compile и dist.lib.copy. Также для создания исполнимого JAR&#39;а необходимо сгенерировать специальный файл MANIFEST.MF, в котором должны быть указаны имя главного класса, содержащего метод main() и CLASSPATH, благодаря которому программа сможет найти все необходимые ей библиотеки. У нас, на момент выполнения цели make.jar, уже есть элемент path, содержащий все пути до файлов используемых библиотек. Проблема в том, что эти пути абсолютные и указывают на конкретное место. Нам для распространения нужно чтобы пути были относительными и указывали на положение файлов библиотек, которые были скопированы с помощью цели dist.lib.copy в каталог с программой. Кроме этого, символом разделителя путей в манифесте, должен быть пробел, а не &quot;:&quot; или &quot;;&quot;. Поэтому, с помощью элемента pathconvert происходит преобразование значения CLASSPATH, использовавшегося для компиляции, в значение пригодное для записи в манифесте. Элемент chainedmapper определяет цепочку преобразований, которые необходимо произвести с элементом path. В этой цепочке находится два действия: flattenmapper и globmapper. Эти действия совершают удаление информации о пути к файлам определенным в path, оставляя только имя файла, и добавляют перед именами файлов относительный путь к каталогу, содержащему эти библиотеки в рамках дистрибутива. Полученный результат преобразования сохраняется в свойстве dist.classpath и выводится на экран. Затем элемент jar используется для создания java-архива. Внутри этого элемента описывается манифест и набор скомпилированных классов для упаковки. В манифесте указываются атрибуты, содержащие имя главного класса и преобразованный CLASSPATH, а также имя того, кто создал этот JAR.&lt;br /&gt;
&lt;br /&gt;
Цель javadoc осуществляет генерацию документации javadoc. Её подробно я описывать не буду, просто привел для демонстрации. &lt;br /&gt;
&lt;br /&gt;
Конечной целью является dist. Её легко понять просто посмотрев на код. В ней происходит копирование файла с описанием программы в каталог с дистрибутивом и создание zip-архива из этого каталога. &lt;br /&gt;
&lt;br /&gt;
Цель clean также проста до безобразия и просто производит удаление результатов предыдущих сборок.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: x-large;&quot;&gt;Заключение&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Хотел написать небольшую статью об основах Ant, а получилось так много букв, что вряд ли кто-то будет читать это до конца :) Зато, вроде бы, получилось очень доступно и подробно. Не смотря на объём, статья не является чем-то большим, чем введение в Ant, но её более чем достаточно для начала работы с этим замечательным инструментом. Более подробно обо всех возможностях Ant можно узнать из &lt;a href=&quot;http://ant.apache.org/manual/index.html&quot;&gt;документации&lt;/a&gt; на официальном сайте проекта.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/5322004759700831823/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2011/09/apache-ant.html#comment-form' title='Комментарии: 12'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/5322004759700831823'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/5322004759700831823'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2011/09/apache-ant.html' title='Основы использования системы сборки Apache Ant'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-3748973465575954509</id><published>2011-08-14T10:08:00.008+04:00</published><updated>2013-08-01T06:25:53.614+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="CUDA"/><category scheme="http://www.blogger.com/atom/ns#" term="Eclipse"/><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="NVIDIA"/><category scheme="http://www.blogger.com/atom/ns#" term="Программирование"/><title type='text'>Вычисление БПФ на GPU с использованием Java и CUDA</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
В последнее время у меня на работе встала задача создания &lt;a href=&quot;http://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B5%D0%BA%D1%82%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0&quot;&gt;спектрограмм&lt;/a&gt; для довольно большого объема данных. Главным инструментом для этого является алгоритм Быстрого Преобразования Фурье. На сегодняшний день для Java написано уже множество библиотек, реализующих вычисление БПФ и другие математические инструменты, например, мне нравится &lt;a href=&quot;http://commons.apache.org/math/&quot;&gt;Commons Math от Apache project&lt;/a&gt;. Но для того, чтобы обрабатывать большие объемы данных с помощью этой библиотеки за более или менее разумное время, нужна довольно мощная и недешевая машина, которой у меня нет. Поэтому я и обратил свой взор на технологию &lt;a href=&quot;http://developer.nvidia.com/category/zone/cuda-zone&quot;&gt;NVIDIA CUDA&lt;/a&gt;, которая позволяет очень шустро производить расчеты с плавающей точкой на чипе видеокарты. А, как известно, БПФ это очень даже плавающая точка :) Вдвойне приятно то, что хорошие люди уже давно написали Java-интерфейс &lt;a href=&quot;http://www.jcuda.org/&quot;&gt;JCuda&lt;/a&gt; для нативной библиотеки CUDA. И ещё больше я обрадовался, когда обнаружил, что библиотека CUDA имеет в своем составе модуль &lt;a href=&quot;http://developer.nvidia.com/cufft&quot;&gt;CUFFT&lt;/a&gt;, который предоставляет средства для расчета БПФ, использующие по полной программе возможности видеокарты для выполнения параллельных вычислений с плавающей точкой.&lt;br /&gt;
&lt;br /&gt;
CUDA поддерживается видеокартами NVIDIA GeForce начиная с чипа G80, т.е. с восьмой серии. Машина, на которой я тестирую в данный момент связку Java + CUDA довольно старенькая и имеет на борту процессор AMD Athlon X2 4200+, видеокарту NVIDIA GeForce 8800 GTS 640 Mb, всего 2 Gb оперативной памяти и управляется операционной системой openSUSE 11.4.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Теперь немного о производительности видеокарт и центральных процессоров в цифрах. Мерой производительности вычислительных систем, которая показывает количество операций с плавающей точкой выполняемых за секунду является единица, называемая FLOPS. Ниже в таблицах можно увидеть на сколько различается теоретическая производительность современных видеокарт и современных процессоров при выполнении операций с плавающей точкой.&lt;br /&gt;
&lt;style type=&quot;text/css&quot;&gt;
table.list {border: black thin solid;}
table.list td, table.list th{border: black thin solid; padding: 5px;}
table.list th.head{text-align: center;}
&lt;/style&gt;&lt;br /&gt;
&lt;center&gt;
&lt;table cellpadding=&quot;10&quot;&gt;&lt;tbody&gt;
&lt;tr&gt; &lt;td&gt;&lt;br /&gt;
&lt;table cellspacing=&quot;0&quot; class=&quot;list&quot;&gt;&lt;tbody&gt;
&lt;tr&gt; &lt;th class=&quot;head&quot; colspan=&quot;2&quot;&gt;Процессоры&lt;/th&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;th&gt;Модель&lt;/th&gt;&lt;th&gt;Гигафлопс&lt;/th&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;AMD Phenom II X6 1100T Black Edition 3.3 ГГц&lt;/td&gt; &lt;td&gt;60.1&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;Intel Core i7-975 XE 3.33 ГГц&lt;/td&gt; &lt;td&gt;53.28&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;Intel Core 2 Quad Q8300 2.5 ГГц&lt;/td&gt; &lt;td&gt;40&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;AMD AMD ATHLON II X4 645 3.1 ГГц&lt;/td&gt; &lt;td&gt;38.44&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;Intel Core 2 Duo 2.4 ГГц&lt;/td&gt; &lt;td&gt;19.2&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;AMD Athlon 64 X2 4200+ 2.2 ГГц&lt;/td&gt; &lt;td&gt;13.2&lt;/td&gt; &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/td&gt;  &lt;td&gt;&lt;br /&gt;
&lt;table cellspacing=&quot;0&quot; class=&quot;list&quot;&gt;&lt;tbody&gt;
&lt;tr&gt; &lt;th class=&quot;head&quot; colspan=&quot;2&quot;&gt;Видеокарты&lt;/th&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;th&gt;Модель&lt;/th&gt;&lt;th&gt;Гигафлопс&lt;/th&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;GeForce GTX 590&lt;/td&gt; &lt;td&gt;2488.3&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;GeForce GTX 580&lt;/td&gt; &lt;td&gt;1581.056&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;GeForce GTX 480&lt;/td&gt; &lt;td&gt;1344.96&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;GeForce GTX 280&lt;/td&gt; &lt;td&gt;933.120&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;GeForce 9800 GTX&lt;/td&gt; &lt;td&gt;648&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;GeForce 8800 GTS 640mb&lt;/td&gt; &lt;td&gt;346&lt;/td&gt; &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/td&gt; &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/center&gt;
&lt;br /&gt;
Как видно из таблиц, теоретическая производительность моей видеокарты в 26 раз превосходит производительность моего процессора и очень уж хочется это превосходство использовать при расчете БПФ. Посмотрим как это сделать.&lt;br /&gt;
&lt;br /&gt;
На данном этапе я пока не разбирался глубоко в возможностях CUDA и использовал только готовый модуль CUFFT и интерфейс к нему из JCuda. Т.е. в моем случае мне не приходилось писать свои собственные ядра (kernels) для CUDA. Что такое ядро CUDA? Это файл с расширением PTX, исходный код которого пишется на языке, являющимся по сути ограниченной версией языка C, а затем компилируется с помощью специального компилятора NVCC. Файл ядра CUDA содержит функции, которые должны выполняться на GPU. С помощью JCuda можно, грубо говоря, загружать такие ядра в вашу java-программу и вызывать функции, описанные в них. &lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Подготовка&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Итак, для того чтобы использовать JCuda, сначала необходимо сделать следующее:&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://www.nvidia.ru/Download/index.aspx?lang=ru&quot;&gt;Скачать&lt;/a&gt; и установить &quot;родные&quot; проприетарные драйвера NVIDIA. &lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://developer.nvidia.com/cuda-toolkit-archive&quot;&gt;Скачать&lt;/a&gt; и установить CUDA Toolkit&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.jcuda.org/downloads/downloads.html&quot;&gt;Скачать&lt;/a&gt; и распаковать библиотеку JCuda&lt;/li&gt;
&lt;li&gt;Добавить путь до jar-файлов JCuda в CLASSPATH или подключить её в вашей IDE как пользовательскую библиотеку&lt;/li&gt;
&lt;li&gt;Установить путь до нативных библиотек JCuda (файлы *.so в linux или *.dll в windows) в переменной окружения LD_LIBRARY_PATH&lt;/li&gt;
&lt;/ol&gt;
&lt;b&gt;Важные замечания&lt;/b&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Сначала я установил Developer Drivers for Linux (260.19.26), ссылка на которые находится на той же странице, где и ссылка на CUDA Toolkit 3.2. Уж не знаю является ли установка именно этой версии драйверов обязательной, но после того, как я убедился, что CUDA работает, я установил последние на тот момент драйвера NVIDIA версии 280.13, которые при установке сообщили о том, что сносят драйвера 260.19.26, потому что сами всё умеют. CUDA при этом осталась работоспособной.&lt;/li&gt;
&lt;li&gt;У меня 32-разрядная версия openSUSE, поэтому мне были нужны  соответствующие версии CUDA и JCuda. Не особо запариваясь, я взял  последнюю доступную собранную для Linux 32-bit версию JCuda 0.3.2a и  соответствующую ей версию CUDA Toolkit 3.2. CUDA Toolkit 3.2 была доступна для версии openSUSE 11.2, но вполне себе благополучно встала на 11.4. Входе установки CUDA Toolkit, предложит выбрать путь установки и по завершению скажет о том, что нужно будет добавить в переменные окружения PATH и LD_LIBRARY_PATH. Сделайте это.&lt;/li&gt;
&lt;li&gt;В Eclipse JCuda можно подключить как пользовательскую библиотеку: Window -&amp;gt; Preferences -&amp;gt; Java -&amp;gt; Build Path -&amp;gt; User Libraries, New, AddJARs... и добавить все файлы *.jar из каталога jcuda. После этого библиотеку можно добавить в проект.&lt;/li&gt;
&lt;li&gt;Необходимо указать путь к нативным библиотекам JCuda. В Eclipse можно поступить двумя способами. Первый способ заключается в том, чтобы при добавлении библиотеки JCuda в User Libraries, для каждого jar-файла указать путь до каталога, в котором лежат нативные библиотеки JCuda (Native Library Location). Второй способ это создать каталог в проекте eclipse и скопировать в него все файлы нативных библиотек JCuda. В Run Configurations проекта добавить переменную окружения LD_LIBRARY_PATH и указать в ней путь до этого каталога. На скриншоте ниже второй способ показан наглядно. &lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6RUSUYGV82WQpUlbwRUj_S_blYoFQElBGmvHOyNjVOCb3PfJdEL-PIAcJopebDIOn53fzmog0YYEow8HEwsYU0_BPKNWV77_OPOpRNQ6ZWV88rvFysL6CsPX3qZGxvMMRbPL-o-Ma3SbU/s1600/eclipse-run-config.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;322&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6RUSUYGV82WQpUlbwRUj_S_blYoFQElBGmvHOyNjVOCb3PfJdEL-PIAcJopebDIOn53fzmog0YYEow8HEwsYU0_BPKNWV77_OPOpRNQ6ZWV88rvFysL6CsPX3qZGxvMMRbPL-o-Ma3SbU/s640/eclipse-run-config.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;От слов к делу&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
CUFFT базируется на очень популярной и эффективной библиотеке &lt;a href=&quot;http://www.fftw.org/&quot;&gt;FFTW&lt;/a&gt;. Довольно интересная концепция этой библиотеки заключается в том, что прежде чем осуществить преобразование какого-либо массива данных, нужно сформировать так называемый план (plan). Такой подход позволяет не тратить время на пересчет конфигурации алгоритма преобразования при множественных однотипных преобразованиях. Чтобы создать план используя JCuda, сначала нужно создать объект cufftHandle, который представляет указатель на план, а потом, используя этот указатель, создать план с помощью какой-либо из функций: cufftPlan1d, cufftPlan2d или cufftPlan3d. Эти методы создают план для выполнения одномерного, двумерного и трехмерного БПФ соответственно. В моем случае необходимо осуществить одномерное (1D) преобразование вещественных чисел и на выходе получить также вещественные числа. В CUFFT &lt;a href=&quot;http://www.jcuda.org/jcuda/jcufft/doc/index.html&quot;&gt;есть несколько вариантов проведения БПФ&lt;/a&gt;:&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;CUFFT_C2C - преобразование комплексных чисел в комплексные (одинарная точность)&lt;/li&gt;
&lt;li&gt;CUFFT_C2R - преобразование комплексных чисел в вещественные (одинарная точность)&lt;/li&gt;
&lt;li&gt;CUFFT_D2Z - преобразование вещественных чисел в комплексные (двойная точность)&lt;/li&gt;
&lt;li&gt;CUFFT_R2C - преобразование вещественных чисел в комплексные (одинарная точность)&lt;/li&gt;
&lt;li&gt;CUFFT_Z2D - преобразование комплексных чисел в вещественные (двойная точность)&lt;/li&gt;
&lt;li&gt;CUFFT_Z2Z - преобразование комплексных чисел в комплексные  (двойная точность)&lt;/li&gt;
&lt;/ul&gt;
В Java тип с одинарной точностью это float, а с двойной - double. Массив комплексных чисел в CUFFT представляет чередующиеся реальные и мнимые части числа. Т.е. для представления одного комплексного числа требуется два элемента массива. Например, массив размером 10, может содержать 5 комплексных чисел.&lt;br /&gt;
&lt;br /&gt;
Как видно выше, в CUFFT нет возможности подать на вход вещественные числа и на выходе получить тоже вещественные, поэтому можно использовать преобразования CUFFT_R2C или CUFFT_D2Z, а затем самостоятельно найти модули полученных комплексных чисел, это уже не такое дорогое действие как выполнение БПФ в плане времени.&lt;br /&gt;
&lt;br /&gt;
Еще оговорюсь, что, к сожалению, моя GeForce 8800 GTS не поддерживает операции с двойной точностью, поэтому мне приходится использовать CUFFT_R2C, хотя хотелось бы CUFFT_D2Z, но это уже мелочи которые лечатся покупкой новой видеокарты.&lt;br /&gt;
&lt;br /&gt;
Пример создания плана для одномерного БПФ вещественных чисел в комплексные:&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;cufftHandle plan = new cufftHandle();
JCufft.cufftPlan1d(plan, inputDataSize, cufftType.CUFFT_R2C, 1);
&lt;/pre&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;plan - указатель на план&lt;/li&gt;
&lt;li&gt;inputDataSize - количество входных значений; если входные данные являются вещественными, то этот параметр равняется длине входного массива, а если входные данные это массив комплексных чисел, то значение параметра равно половине длины входного массива, т.к. одно комплексное число представлено двумя элементами массива&lt;/li&gt;
&lt;li&gt;cufftType.CUFFT_R2C - тип преобразования&lt;/li&gt;
&lt;li&gt;1 - количество подобных преобразований&lt;/li&gt;
&lt;/ul&gt;
После создания плана можно уже выполнять преобразование, но только с одинарной точностью, и данные в таком случае будут находиться в оперативной памяти, а не в памяти видеокарты:&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;// создание случайных входных данных размером 1024
float[] inputData = createRandomData(1024);
// массив для сохранения результатов преобразования
float[] fftResults = new float[inputData.length + 2];
// выполнение преобразования
JCufft.cufftExecR2C(plan, inputData, fftResults);
&lt;/pre&gt;
В этом коде можно заметить некоторую странность. Почему размер массива для сохранения результатов преобразования на 2 элемента больше, чем размер входных данных? Важно в этом разобраться. На входе мы имеем массив из N = 1024 вещественных чисел. А результатом у нас будут комплексные числа, т.е. по 2 элемента массива на одно число. Но в тоже время в документации CUFFT сказано, что результатом преобразования является массив без избыточных значений и его размер равен N/2 + 1. Вот тут для меня небольшая загадка. Почему +1? Зачем он нужен? На сколько я знаю, полезными результатами БПФ является массив размером равным ровно половине размера входного массива, т.е. N/2. Поэтому этот +1 элемент может является особенностью алгоритма, а может это пробел в моих знаниях :) Но это и не важно. Важно помнить то, что результатами преобразований CUFFT является массив который содержит N/2 + 1 значений. Но почему тогда в примере выше размер выходного массива равен не N/2 + 1, а N + 2? Это потому, что на выходе у нас комплексные числа, и для их представления, как было уже много раз упомянуто выше, необходимо два элемента массива. Если писать полностью, то правильно было бы вычислять размер выходного массива так: (inputData.length/2 + 1) * 2. Если раскрыть скобки, то мы и получим inputData.length + 2.&lt;br /&gt;
&lt;br /&gt;
Для того, чтобы осуществлять преобразования с двойной точностью или еще увеличить производительность, необходимо скопировать массив входных данных в память видеокарты и после преобразования результаты скопировать обратно:&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;// создание случайных входных данных размером 1024
float[] inputData = createRandomData(1024);
// массив для сохранения результатов преобразования
float[] fftResults = new float[inputData.length + 2];
// указатель на устройство
Pointer deviceDataIn = new Pointer();
// выделение памяти на устройстве для входных данных
JCuda.cudaMalloc(deviceDataIn, inputData.length * 4);
// копирование данных из оперативной памяти в память видеокарты
JCuda.cudaMemcpy(deviceDataIn, Pointer.to(inputData), inputData.length * 4, 
          cudaMemcpyKind.cudaMemcpyHostToDevice);
     
Pointer deviceDataOut = new Pointer();
// выделение памяти на устройстве для результатов
JCuda.cudaMalloc(deviceDataOut, fftResults.length * 4);

// создание плана     
cufftHandle plan = new cufftHandle();
JCufft.cufftPlan1d(plan, inputData.length, cufftType.CUFFT_R2C, 1);

// преобразование
JCufft.cufftExecR2C(plan, deviceDataIn, deviceDataOut);

// копирование данных из памяти видеокарты в оперативную память     
JCuda.cudaMemcpy(Pointer.to(fftResults), deviceDataOut, fftResults.length * 4, 
             cudaMemcpyKind.cudaMemcpyDeviceToHost);
&lt;/pre&gt;
В данном примере метод &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;JCuda.cudaMalloc(Pointer devPtr, long size)&lt;/span&gt;&lt;/b&gt; осуществляет выделение памяти размером &lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;size&lt;/b&gt; байт на видеокарте и возвращает на неё указатель в параметре &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;devPtr&lt;/span&gt;&lt;/b&gt;. &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;JCuda.cudaMemcpy(Pointer dst, Pointer src, long count, int cudaMemcpyKind_kind)&lt;/span&gt;&lt;/b&gt; осуществляет копирование данных между хостом и видеокартой, где &lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;dst&lt;/b&gt; - указатель на место назначения, &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;src&lt;/span&gt;&lt;/b&gt; - указатель на источник, &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;count&lt;/span&gt;&lt;/b&gt; - количество байт для копирования, &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;cudaMemcpyKind_kind &lt;/span&gt;&lt;/b&gt;- направление копирования (например, &lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;cudaMemcpyKind.cudaMemcpyDeviceToHost&lt;/b&gt; для копирования данных с памяти видеокарты в оперативную память). С помощью &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;Pointer.to(float[] values)&lt;/span&gt;&lt;/b&gt; создается указатель на массив значений.&lt;br /&gt;
&lt;br /&gt;
После проведения преобразования необходимо уничтожить все указатели и планы:&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;JCufft.cufftDestroy(plan);
JCuda.cudaFree(deviceDataIn);
JCuda.cudaFree(deviceDataOut);
&lt;/pre&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Пример программы&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Теперь приведу пример рабочей программы, которая будет генерировать массив случайных значений размером 2 в 23 степени (кратность степени двойки необходима для библиотеки от Apache, для CUFFT это условие не обязательно) и производить БПФ этого массива с помощью Apache Commons Math и CUDA, замеряя при этом время выполнения каждого преобразования. В конце программа выведет первое, среднее и последнее значение для каждого из результатов преобразования, чтобы можно было убедиться в правильности расчетов. Для того, чтобы программа успешно выполнилась, &lt;u&gt;необходимо&lt;/u&gt; увеличить размер кучи у JVM с помощью параметров командной строки &lt;b&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;-Xms512m -Xmx768m&lt;/span&gt;&lt;/b&gt; или же уменьшить размер генерируемого входного массива. В Eclipse указать параметры командной строки можно в Run Configurations во вкладке Arguments, раздел VM arguments.&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;import java.util.Date;
import java.util.Random;
import org.apache.commons.math.complex.Complex;
import org.apache.commons.math.transform.FastFourierTransformer;
import jcuda.Pointer;
import jcuda.jcufft.JCufft;
import jcuda.jcufft.cufftHandle;
import jcuda.jcufft.cufftType;
import jcuda.runtime.JCuda;
import jcuda.runtime.cudaMemcpyKind;

public class JCufftDemo {

 public static void main(String[] args) {

  double[] fftResults;  
  int dataSize = 1&amp;lt;&amp;lt;23;

  System.out.println(&quot;Генерация входных данных размером &quot;+dataSize+&quot; значений...\n&quot;);
  float[] inputData = createRandomData(dataSize);
  
  System.out.println(&quot;1D БПФ с использованием apache commons math...&quot;);
  fftResults = commonsTransform(floatDataToDoubleData(inputData.clone()));
  printSomeValues(fftResults);
  
  System.out.println();
  System.out.println(&quot;1D БПФ JCufft (данные в оперативной памяти)...&quot;);
  fftResults = jcudaTransformHostMemory(inputData.clone());
  printSomeValues(fftResults);
   
  System.out.println();
  System.out.println(&quot;1D БПФ JCufft (данные в памяти видеокарты)...&quot;);
  fftResults = jcudaTransformDeviceMemory(inputData.clone());
  printSomeValues(fftResults);
 }
 
 /**
  * Генерирует массив случайных чисел
  * 
  * @param dataSize - размер генерируемого массива
  * @return массив случайных чисел
  */
 public static float[] createRandomData(int dataSize){
        Random random = new Random();
        float data[] = new float[dataSize];
        
        for (int i = 0; i &amp;lt; dataSize; i++)
         data[i] = random.nextFloat();

        return data;
 }
 
 /**
  * Конвертирует массив значений типа float в массив значений double
  * 
  * @param data - массив который нужно конвертировать
  * @return - сконвертированный массив
  */
 public static double[] floatDataToDoubleData(float[] data){ 
  
  double[] doubleData = new double[data.length];  
  for(int i=0; i &amp;lt; data.length; i++) doubleData[i] = data[i];
  
  return doubleData;
 }
 
 /**
  * Выполняет БПФ массива значений с помощью CUDA, осуществляя
  * операции с данными в оперативной памяти
  * 
  * @param inputData - массив входных значений
  * @return массив с результатами БПФ
  */
 public static double[] jcudaTransformHostMemory(float[] inputData){
  float[] fftResults = new float[inputData.length + 2];
     // создание плана
     cufftHandle plan = new cufftHandle();
     JCufft.cufftPlan1d(plan, inputData.length, cufftType.CUFFT_R2C, 1);
     // выполнение БПФ
     long timeStart = new Date().getTime();
     JCufft.cufftExecR2C(plan, inputData, fftResults);
     System.out.println(&quot;Время преобразования: &quot; + (new Date().getTime() - timeStart)/1000.0+&quot; сек&quot;);
     // уничтожение плана
     JCufft.cufftDestroy(plan);

  return cudaComplexToDouble(fftResults);
 }
 
 /**
  * Выполняет БПФ массива значений с помощью CUDA, осуществляя
  * операции с данными в памяти видеокарты
  * 
  * @param inputData - массив входных значений
  * @return массив с результатами БПФ
  */
 public static double[] jcudaTransformDeviceMemory(float[] inputData){

     float[] fftResults = new float[inputData.length + 2];

     // указатель на устройство
     Pointer deviceDataIn = new Pointer();
     // выделение памяти на видеокарте для входных данных
     JCuda.cudaMalloc(deviceDataIn, inputData.length * 4);
     // копирование входных данных в память видеокарты
     JCuda.cudaMemcpy(deviceDataIn, Pointer.to(inputData), inputData.length * 4, 
          cudaMemcpyKind.cudaMemcpyHostToDevice);
     
     Pointer deviceDataOut = new Pointer();
     // выделение памяти на видеокарте для результатов преобразования
     JCuda.cudaMalloc(deviceDataOut, fftResults.length * 4);

     // создание плана
     cufftHandle plan = new cufftHandle();
     JCufft.cufftPlan1d(plan, inputData.length, cufftType.CUFFT_R2C, 1);

     // выполнение БПФ
     long timeStart = new Date().getTime();
     JCufft.cufftExecR2C(plan, deviceDataIn, deviceDataOut);
     System.out.println(&quot;Время преобразования: &quot; + (new Date().getTime() - timeStart)/1000.+&quot; сек&quot;);
     
     // копирование результатов из памяти видеокарты в оперативную память
     JCuda.cudaMemcpy(Pointer.to(fftResults), deviceDataOut, fftResults.length * 4, 
             cudaMemcpyKind.cudaMemcpyDeviceToHost);

     // освобождение ресурсов
     JCufft.cufftDestroy(plan);
     JCuda.cudaFree(deviceDataIn);
     JCuda.cudaFree(deviceDataOut);

  return cudaComplexToDouble(fftResults);
 }
 
 /**
  * Выполняет БПФ массива значений с помощью Apache Commons Math
  * 
  * @param inputData - массив входных значений
  * @return массив с результатами БПФ
  */
 public static double[] commonsTransform(double[] inputData){
  
  FastFourierTransformer fft = new FastFourierTransformer();
  long timeStart = new Date().getTime();
  Complex[] cmx = fft.transform(inputData);
  System.out.println(&quot;Время преобразования: &quot; + (new Date().getTime() - timeStart)/1000.+&quot; сек&quot;);

  double[] fftReults = new double[inputData.length/2 + 1];
  for(int i = 0; i &amp;lt; fftReults.length; i++){
   fftReults[i] = cmx[i].abs();
  }
  
  return fftReults;
 }
 
    /**
     * Метод осуществляет преобразование массива комплексных чисел в
     * массив, содержащий их модули
     * 
     * @param complexData - массив комплексных чисел
     * @return массив модулей комплексных чисел
     */
    public static double[] cudaComplexToDouble(float[] complexData){

     double[] result = new double[complexData.length/2];
     int j=0;
        for(int i=0; i &amp;lt; complexData.length-1; i++) {         
         result[j++] = Math.sqrt(complexData[i]*complexData[i] + complexData[i+1]*complexData[i+1]);
         i++;
        }

     return result;  
    }
    
    /**
     * Выводит на стандартный вывод первое, среднее 
     * и последнее значения массива
     * 
     * @param data - массив, значения которого необходимо вывести
     */
    public static void printSomeValues(double[] data){    
     System.out.println(&quot;[0]: &quot;+data[0]);
     System.out.println(&quot;[&quot;+(data.length/2)+&quot;]: &quot;+data[data.length/2]);
     System.out.println(&quot;[&quot;+(data.length-1)+&quot;]: &quot;+data[data.length-1]);
    }
}
&lt;/pre&gt;
Результат выполнения программы:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;Генерация входных данных размером 8388608 значений...&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;1D БПФ с использованием apache commons math...&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;Время преобразования: 92.851 сек&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;[0]: 4195107.645827353&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;[2097152]: 1411.8392665442454&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;[4194304]: 114.60465306043625&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;1D БПФ JCufft (данные в оперативной памяти)...&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;Время преобразования: 0.085 сек&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;[0]: 4195107.922955976&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;[2097152]: 1411.791370918522&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;[4194304]: 114.75&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;1D БПФ JCufft (данные в памяти видеокарты)...&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;Время преобразования: 0.0 сек&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;[0]: 4195107.922955976&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;[2097152]: 1411.791370918522&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;[4194304]: 114.75&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
Как видно из результатов выполнения программы, выполнение БПФ с помощью CUDA на несколько порядков быстрее, чем при использовании Apache Commons Math, а при использовании памяти видеокарты оно вообще происходит меньше миллисекунды, что,&amp;nbsp; согласитесь, просто &quot;сносит башню&quot;! :) Но тут необходимо принять во внимание тот факт, что в программе измеряется только выполнение самого преобразования и не учитываются временные издержки на нахождение модуля комплексных данных, выделение памяти под массивы, копирование данных между видеокартой и хостом и т.п., а эти издержки имеют место быть. Поэтому если у вас есть задача в которой вам пригодилось бы использование CUFFT, то лучше сначала провести исследование стоит ли вам использовать память видеокарты или нет. Однако, если нужна двойная точность, то вариантов кроме как копировать данные на видеокарту и обратно нет. Кстати, по поводу двойной точности. В результате выполнения программы можно заметить, что результаты преобразование с помощью Apache Commons Math несколько отличаются от результатов CUFFT. Это происходит как раз из-за того, что в Commons используется двойная точность, а в CUFFT мне пришлось использовать одинарную по причине староватости видеокарты.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/3748973465575954509/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2011/08/gpu-java-cuda.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/3748973465575954509'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/3748973465575954509'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2011/08/gpu-java-cuda.html' title='Вычисление БПФ на GPU с использованием Java и CUDA'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6RUSUYGV82WQpUlbwRUj_S_blYoFQElBGmvHOyNjVOCb3PfJdEL-PIAcJopebDIOn53fzmog0YYEow8HEwsYU0_BPKNWV77_OPOpRNQ6ZWV88rvFysL6CsPX3qZGxvMMRbPL-o-Ma3SbU/s72-c/eclipse-run-config.png" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-7040789294722519091</id><published>2011-07-23T15:18:00.001+04:00</published><updated>2011-07-23T15:20:17.610+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BUGS"/><category scheme="http://www.blogger.com/atom/ns#" term="KDE"/><category scheme="http://www.blogger.com/atom/ns#" term="kdesu"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="openSUSE"/><title type='text'>Баг kdesu в openSUSE 11.4</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;Столкнулся в openSUSE 11.4 с тем, что мои замечательные &lt;a href=&quot;http://eqlbin.blogspot.com/2010/11/bash-kdesu-kdialog.html&quot;&gt;скрипты выбора конфигурации сети&lt;/a&gt;, вдруг перестали нормально работать. Довольно быстро мне стало понятно, что сердце неприятности заключается в программе &lt;b&gt;kdesu&lt;/b&gt;. Здравствуй, новый KDE! Суть проблемы была в том, что после ввода в диалоговом окне пароля суперпользователя ничего не происходило, а должен был запуститься скрипт с соответствующими правами. Я перекопал весь интернет и не нашел ничего лучше, чем удалить (а точнее просто переименовать на всякий случай) файл &lt;b&gt;/usr/lib/kde4/libexec/kdesud&lt;/b&gt;. После этого всё вроде бы заработало, но теперь kdesu стал ругаться, что не может найти своего друга-демона и что это вроде как небезопасно. Ну и хрен с ним, пусть боится.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/7040789294722519091/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2011/07/kdesu-opensuse-114.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/7040789294722519091'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/7040789294722519091'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2011/07/kdesu-opensuse-114.html' title='Баг kdesu в openSUSE 11.4'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-4151189505875891656</id><published>2011-07-22T06:26:00.003+04:00</published><updated>2012-04-11T05:47:03.062+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="messengers"/><category scheme="http://www.blogger.com/atom/ns#" term="qutIM"/><category scheme="http://www.blogger.com/atom/ns#" term="Soft"/><title type='text'>Instant Messenger&#39;ы в Linux. Сборка qutIM 0.3 beta в openSUSE 11.4</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: right;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCGwKHB3ijBh337QPmU2RIa6iuhgMTZrZJlR64SfXYpJH_RbXv_AsR2jkgLps4nKGlg9DT-hKvUs0loH6xIjO6L8EhjJmNzBBWlEiazCYHBVMF0_r1lbymsOGRKHHa56ZeDb8wZ2ms2gyq/s1600/instant-messengers-173x200.jpeg&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCGwKHB3ijBh337QPmU2RIa6iuhgMTZrZJlR64SfXYpJH_RbXv_AsR2jkgLps4nKGlg9DT-hKvUs0loH6xIjO6L8EhjJmNzBBWlEiazCYHBVMF0_r1lbymsOGRKHHa56ZeDb8wZ2ms2gyq/s1600/instant-messengers-173x200.jpeg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
По моему сугубо личному мнению, в Linux, если рассматривать его как систему для десктопного использования, существует две бросающихся в глаза проблемы: отсутствие приятных в использовании мультимедиа проигрывателей и отсутствие таких же приятных Instant Messenger&#39;ов. В этой статье я хочу поговорить о последних.&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Не знаю кому как, но мне всем известные Kopete и Pidgin кажутся страшненькими и неудобными. Хочется что-то попроще, но симпатичное. Что-то вроде того, чем раньше был QIP, только многопротокольное. И достаточно давно я открыл для себя &lt;a href=&quot;http://qutim.org/&quot;&gt;qutIM&lt;/a&gt;. Думаю, что сегодня у него уже достаточно много пользователей. Первая версия, которую я себе поставил была 0.1 (то ли альфа то ли бета). Когда я её увидел, то сразу понял, что это то, чего мне так не хватало для счастья. Конечно, глючило это создание прилично, но мне было всё равно, главное, что глаз радовало.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Потом была версия 0.2, её я и использую до сих пор. Глючит вроде поменьше и в целом стало лучше. Хочется сказать огромное спасибо ребятам, которые в своё свободное время продолжают развивать этот проект.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Несколько дней назад я решил попробовать 0.3 beta, и тут меня поджидало разочарование. Внешне всё сильно изменилось, и для меня не в лучшую сторону. Вдобавок появилось очень много новеньких глюков. Понятно, конечно, что это бета-версия, но дело даже не в глюках. Я по-прежнему готов с ними мириться. Такое чувство, что проект пошел в ту же сторону, где сидят Kopete и Pidgin. Я говорю, главным образом, о простоте и органичности интерфейса, а также о понятности настроек. Пока что я решил остаться на версии 0.2, но продолжу следить за проектом, вдруг снова станет лучше. А пока расскажу, как собрать 0.3b в openSUSE 11.4, и делается это довольно просто, что приятно.&lt;/div&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Сначала ставим все необходимые для сборки пакеты:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;zypper install cmake make gcc git libqt4 libqt4-devel xscreensaver libidn libidn-devel libpurple-devel libqca2 libqca2-devel cyrus-sasl cyrus-sasl-devel libQtWebKit4 libQtWebKit-devel &lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
Создаем себе какой-нибудь каталог и переходим в него, например:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;mkdir ~/SRC&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;cd ~/SRC&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
Тащим исходники с git-репозитория:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;git clone git://github.com/euroelessar/qutim.git&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;cd qutim&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;git submodule update --init --recursive&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
На &lt;a href=&quot;http://wiki.qutim.org/ru/building_from_git&quot;&gt;вики qutIM&lt;/a&gt; сказано, что если при выполнении &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;git submodule update --init --recursive&lt;/span&gt; возникают ошибки, то необходимо запустить команду еще раз. У меня ошибок не было.&lt;/div&gt;
&lt;br /&gt;
Создаем каталог для билда и переходим в него, например:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;mkdir ~/SRC/qutim-build&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;cd ~/SRC/qutim-build&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
Запускаем cmake:&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;cmake ~/SRC/qutim&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
Сначала я не установил пакет libQtWebKit-devel и cmake руганулся так:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;CMake Error: The following variables are used in this project, but they are set to NOTFOUND.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;Please set them or make sure they are set and tested correctly in the CMake files:&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;QT_QTDECLARATIVE_INCLUDE_DIR (ADVANCED)&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;used as include directory in directory /home/eqlbin/SRC/qutim/plugins/qmlchat&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;QT_QTWEBKIT_INCLUDE_DIR (ADVANCED)&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;used as include directory in directory /home/eqlbin/SRC/qutim/core/src/corelayers/kineticscroller&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;used as include directory in directory /home/eqlbin/SRC/qutim/core/src/corelayers/adiumchat/chatfactories/webkitchat&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;QT_QTXMLPATTERNS_INCLUDE_DIR (ADVANCED)&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;used as include directory in directory /home/eqlbin/SRC/qutim/plugins/yandexnarod&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;-- Configuring incomplete, errors occurred!&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Если у вас происходит нечто вроде этого, то внимательно посмотрев на имена директорий выше, можно понять какие плагины не хотят собираться. В таком случае их можно отключить, запустив cmake следующим образом:&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;cmake -DQMLCHAT=0 -DYANDEXNAROD=0 ~/SRC/qutim&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
Если же cmake прошел удачно, то запускаем make:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
make&lt;/div&gt;
&lt;br /&gt;
Для установки необходимо выполнить make install с правами root:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
&lt;/div&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
make install&lt;/div&gt;
&lt;br /&gt;
Теперь можно запустить qutIM из командной строки:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;
qutim&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
В заключении хочу сказать, что около года назад я начал следить ещё за одним довольно интересным проектом &lt;a href=&quot;http://mdc.ru/&quot;&gt;MDC&lt;/a&gt;. Это также мультипротокольный месенджер, который выглядит немного странно, на мой взгляд, но очень даже достойно. На официальном сайте есть статическая сборка, которая на openSUSE 11.4 запускается без проблем, хотя глюки в работе тоже случаются, конечно, куда же без них :)&lt;/div&gt;
&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/4151189505875891656/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2011/07/instant-messenger-linux-qutim-03-beta.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/4151189505875891656'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/4151189505875891656'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2011/07/instant-messenger-linux-qutim-03-beta.html' title='Instant Messenger&#39;ы в Linux. Сборка qutIM 0.3 beta в openSUSE 11.4'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCGwKHB3ijBh337QPmU2RIa6iuhgMTZrZJlR64SfXYpJH_RbXv_AsR2jkgLps4nKGlg9DT-hKvUs0loH6xIjO6L8EhjJmNzBBWlEiazCYHBVMF0_r1lbymsOGRKHHa56ZeDb8wZ2ms2gyq/s72-c/instant-messengers-173x200.jpeg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-1516421015194864052</id><published>2011-04-21T07:58:00.004+04:00</published><updated>2011-09-28T08:27:40.551+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bash"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="красиво"/><title type='text'>Как узнать полный путь до запущенного bash-скрипта</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;Иногда нужно знать, в каком каталоге находится запущенный скрипт. Ну, например, в этом же каталоге лежат другие скрипты, которые должны быть запущены текущим выполняющемся скриптом. Для того, чтобы он их запустил, ему было бы неплохо знать, где их искать, потому что вы могли запустить скрипт находясь в совершенно произвольной директории, или воспользоваться для запуска не самим файлом скрипта, а символической ссылкой, лежащей в другом каталоге, которая указывает на файл скрипта.&lt;br /&gt;
&lt;br /&gt;
Можно, конечно, поступить просто и в самом скрипте в переменной жёстко прописать полный путь до каталога, в котором этот скрипт лежит. Получится что-то вроде этого:&lt;br /&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;#!/bin/bash

DIRECTORY=&quot;/home/user/my_scripts&quot;
cd $DIRECTORY

# запуск &quot;соседних&quot; скриптов
./script1.sh
./script2.sh
&lt;/pre&gt;Вполне рабочий вариант и две обозначенные выше проблемы будут решены, но если надо будет переместить скрипт в другой каталог, то и значение переменной придётся руками поменять в самом скрипте. Не очень удобно. &lt;br /&gt;
&lt;br /&gt;
Для красивого решения проблемы нужно знать всего три вещи:&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Путь до выполняющегося скрипта можно узнать с помощью $0, но проблема в том, что он относительный, т.е. если вы запустите скрипт как ./script.sh, то и $0 будет содержать ./script.sh&lt;/li&gt;
&lt;li&gt;Команда &lt;b&gt;readlink&lt;/b&gt; с параметром &lt;b&gt;-e&lt;/b&gt; решит сразу две проблемы: во-первых она вернет полный путь до файла скрипта, если вы воспользовались для запуска символической ссылкой (даже если это была цепочка симлинков), а во-вторых преобразует относительный путь, если такой получен с помощью $0, в абсолютный&lt;/li&gt;
&lt;li&gt;Чтобы избавиться от имени файла скрипта в конце абсолютного пути, нужно воспользоваться командой &lt;b&gt;dirname&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;/ol&gt;Пример:&lt;br /&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;#!/bin/bash

# полный путь до скрипта
ABSOLUTE_FILENAME=`readlink -e &quot;$0&quot;`
# каталог в котором лежит скрипт
DIRECTORY=`dirname &quot;$ABSOLUTE_FILENAME&quot;`

# запуск &quot;соседних&quot; скриптов
$DIRECTORY/script1.sh
$DIRECTORY/script2.sh
&lt;/pre&gt;&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/1516421015194864052/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2011/04/bash.html#comment-form' title='Комментарии: 13'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/1516421015194864052'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/1516421015194864052'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2011/04/bash.html' title='Как узнать полный путь до запущенного bash-скрипта'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-6155062134757307880</id><published>2011-04-04T08:07:00.004+04:00</published><updated>2011-09-28T08:37:34.955+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="web"/><category scheme="http://www.blogger.com/atom/ns#" term="www.blogger.com"/><category scheme="http://www.blogger.com/atom/ns#" term="красиво"/><title type='text'>Прикручиваем кнопки &quot;Поделиться&quot; от Яндекса к www.blogger.com</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;Захотелось мне прикрутить к блогу эти самые кнопочки. Напишу пару слов о том, как я это сделал.&lt;br /&gt;
&lt;br /&gt;
Сначала идём по адресу &lt;a href=&quot;http://api.yandex.ru/share/&quot;&gt;http://api.yandex.ru/share/&lt;/a&gt;, настраиваем вид блока &quot;Поделиться&quot; и выбираем сервисы, в которые хочется постить с его помощью. Получаем код, вроде этого:&lt;br /&gt;
&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: html&quot;&gt;&lt;![CDATA[
&lt;script charset=&quot;utf-8&quot; src=&quot;//yandex.st/share/share.js&quot; type=&quot;text/javascript&quot;/&gt;
&lt;div class=&quot;yashare-auto-init&quot; data-yasharel10n=&quot;ru&quot; data-yasharequickservices=&quot;yaru,vkontakte,facebook,twitter,odnoklassniki,moimir,lj&quot; data-yasharetype=&quot;none&quot;&gt;
&lt;/div&gt;]]&gt;&lt;/script&gt;&lt;br /&gt;
Теперь заходим в &lt;b&gt;Панель инструментов -&amp;gt; Дизайн -&amp;gt; Изменить HTML&lt;/b&gt;. Ставим галку рядом с &quot;&lt;b&gt;Расширить шаблоны виджета&lt;/b&gt;&quot;.&lt;br /&gt;
&lt;br /&gt;
Между тегами &amp;lt;head&amp;gt; и &amp;lt;/head&amp;gt; где-нибудь втыкаем первую строку из листинга выше. Короче эту: &lt;br /&gt;
&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: html&quot;&gt;&lt;![CDATA[
&lt;script charset=&quot;utf-8&quot; src=&quot;//yandex.st/share/share.js&quot; type=&quot;text/javascript&quot;/&gt;
]]&gt;&lt;/script&gt;&lt;br /&gt;
Дальше надо выбрать место, где хочется разместить эти кнопки. Я решил вставить их под сообщением, ниже автора и времени создания сообщения. Если вас устраивает это место, то ищите в шаблоне строку &lt;b&gt;&amp;lt;span class=&quot;post-icons&quot;&amp;gt; &lt;/b&gt;и &lt;u&gt;над&lt;/u&gt; ней вставляйте оставшуюся часть полученного кода:&lt;br /&gt;
&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: html&quot;&gt;&lt;![CDATA[
&lt;div class=&quot;yashare-auto-init&quot; data-yasharel10n=&quot;ru&quot; data-yasharequickservices=&quot;yaru,vkontakte,facebook,twitter,odnoklassniki,moimir,lj&quot; data-yasharetype=&quot;none&quot;&gt;
&lt;/div&gt;]]&gt;&lt;/script&gt;&lt;br /&gt;
Если вы сохраните шаблон и зайдёте на блог, то кнопочки должны будут появиться и будут даже работать, только не всегда правильно. Например, если отдельно открыть страницу с конкретным сообщением блога и там нажать на какую-либо из кнопок &quot;поделиться&quot;, то всё отработает правильно. А вот если нажать на одну из этих кнопок под каким-либо сообщением на главной странице, то делёжка будет осуществляться не ссылкой на это сообщение, а ссылкой на главную страницу блога. Для того, чтобы такого безобразия не происходило, нужно элементу &lt;b&gt;DIV&lt;/b&gt; добавить атрибуты &lt;b&gt;data-yashareLink&lt;/b&gt; и &lt;b&gt;data-yashareTitle&lt;/b&gt;, которые позволяют установить ссылку, которой нужно поделиться и заголовок сообщения, соответственно. Но для того, чтобы динамически получать ссылку и заголовок нужно использовать хитрые выражения, которые понимает движок блоггера.&lt;br /&gt;
&lt;br /&gt;
Вот как это выглядит:&lt;br /&gt;
&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: html&quot;&gt;&lt;![CDATA[
&lt;div class=&quot;yashare-auto-init&quot; data-yasharel10n=&quot;ru&quot; data-yasharequickservices=&quot;yaru,vkontakte,facebook,twitter,odnoklassniki,moimir,lj&quot; data-yasharetype=&quot;none&quot; expr:data-yasharelink=&quot;data:post.url&quot; expr:data-yasharetitle=&quot;data:post.title&quot;&gt;
&lt;/div&gt;]]&gt;&lt;/script&gt;&lt;br /&gt;
В процессе генерации страниц блога выражение &lt;b&gt;expr:data-yashareLink=&#39;data:post.url&#39; &lt;/b&gt;будет заменено блоггером на &lt;b&gt;data-yashareLink=&#39;http://myblog.blogspot.com/путь_до_сообщения&#39;&lt;/b&gt;, а &lt;b&gt;expr:yashareTitle=&#39;data:post.title&#39;&lt;/b&gt; на &lt;b&gt;yashareTitle=&#39;Заголовок_сообщения&#39;&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
Кстати, контейнером для кнопок может быть не обязательно элемент &lt;b&gt;DIV&lt;/b&gt;, я использую &lt;b&gt;SPAN&lt;/b&gt;. Вот код, который я добавил себе в шаблон:&lt;br /&gt;
&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: html&quot;&gt;&lt;![CDATA[
&lt;span&gt;Поделиться: &lt;/span&gt;
&lt;span class=&#39;yashare-auto-init&#39; data-yashareL10n=&#39;ru&#39; data-yashareQuickServices=&#39;yaru,vkontakte,facebook,twitter,odnoklassniki,moimir,lj,blogger&#39; data-yashareType=&#39;none&#39; expr:data-yashareLink=&#39;data:post.url&#39; expr:data-yashareTitle=&#39;data:post.title&#39;&gt;&lt;/span&gt;
]]&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/6155062134757307880/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2011/04/wwwbloggercom.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/6155062134757307880'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/6155062134757307880'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2011/04/wwwbloggercom.html' title='Прикручиваем кнопки &quot;Поделиться&quot; от Яндекса к www.blogger.com'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-2053650672405256671</id><published>2011-03-25T02:53:00.004+03:00</published><updated>2011-09-28T08:40:19.005+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bash"/><category scheme="http://www.blogger.com/atom/ns#" term="iptables"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="Программирование"/><category scheme="http://www.blogger.com/atom/ns#" term="администрирование"/><category scheme="http://www.blogger.com/atom/ns#" term="красиво"/><title type='text'>Красивое использование косвенной адресации в bash-скриптах</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;Косвенная адресация это когда, грубо говоря, одна переменная ссылается на какое-либо значение через другую переменную. Если говорить точнее и применительно к bash&#39;у, то это когда одна переменная хранит имя другой переменной, которая в свою очередь содержит, например, какое-либо значение. С помощью косвенной адресации можно, используя первую переменную, сразу получить доступ к значению, которое содержит вторая переменная. Как это работает вроде бы ясно, но, возможно, не сразу понятна нужность всего этого.&lt;br /&gt;
&lt;br /&gt;
Итак, допустим у нас есть переменная X, которая содержит строку &quot;значение&quot;. Так же есть переменная Y, которая в качестве значения содержит имя переменной X. Красивость заключается в том, что с помощью прямой и косвенной адресации, используя переменную Y, можно получить соответственно имя и значение переменной X.&lt;br /&gt;
&lt;br /&gt;
Простой пример:&lt;br /&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;X=&quot;значение&quot;
Y=X
echo &quot;$Y: ${!Y}&quot;
&lt;/pre&gt;В этом примере с помощью прямой адресации &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;$Y&lt;/span&gt; мы получаем доступ к имени переменной X, а с помощью косвенной &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;${!Y}&lt;/span&gt; - к её значению. В старых версиях bash косвенная адресация могла использоваться только так &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;eval Z=\$$Y&lt;/span&gt;, а начиная со 2 версии она стала выглядеть проще, хотя старый вариант так же может использоваться.&lt;br /&gt;
&lt;br /&gt;
Теперь приведу более жизненный пример. У меня есть скрипт, который настраивает iptables. Косвенная адресация в нем используется для того, чтобы сделать проще изменение настроек скрипта и создать красивый вывод. Приведу пример поясняющий основную суть.&lt;br /&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;#!/bin/bash

# список сервисов и их портов
SSH=22
HTTP=80
HTTPS=443
SAMBA=&quot;137 138 139 445&quot;

# локальные сервисы на которые разрешен доступ снаружи
INPUT_PORTS_ACCEPT=&quot;SSH SAMBA HTTP HTTPS&quot;

echo &quot;Удаленный доступ к сервисам хоста:&quot;
# открываем доступ на разрешенные сервисы
for SERVICE_NAME in $INPUT_PORTS_ACCEPT
do
    # для каждого порта сервиса добавляем разрешающие правила в iptables
    PORTS=${!SERVICE_NAME} # устаревший вариант: eval PORTS=\$$SERVICE_NAME
    for PORT in $PORTS
    do
        iptables -t filter -A INPUT -i eth0 -p tcp --dport $PORT -j ACCEPT
        iptables -t filter -A INPUT -i eth0 -p udp --dport $PORT -j ACCEPT
    done

    # выводим имя и порты сервиса
    echo &quot;$SERVICE_NAME: $PORTS --accept&quot;
done
&lt;/pre&gt;Вывод скрипта:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;Удаленный доступ к сервисам хоста:&lt;/div&gt;&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;SSH: 22 --accept&lt;/div&gt;&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;SAMBA: 137 138 139 445 --accept&lt;/div&gt;&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;HTTP: 80 --accept&lt;/div&gt;&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;HTTPS: 443 --accept&lt;/div&gt;&lt;br /&gt;
Таким образом, для настройки скрипта в переменной INPUT_PORTS_ACCEPT нужно указывать не номера портов, а просто имена сервисов, и в выводе наглядно показано на какие локальные сервисы открыт доступ и какие порты им соответствуют. Красиво :) &lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/2053650672405256671/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2011/03/bash.html#comment-form' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/2053650672405256671'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/2053650672405256671'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2011/03/bash.html' title='Красивое использование косвенной адресации в bash-скриптах'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6847658947227037305.post-4676525671341156671</id><published>2011-03-11T06:35:00.005+03:00</published><updated>2011-09-28T08:42:44.786+04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="jdom"/><category scheme="http://www.blogger.com/atom/ns#" term="XML"/><category scheme="http://www.blogger.com/atom/ns#" term="Программирование"/><title type='text'>JDOM: работа с XML на Java - проще простого</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;&lt;a href=&quot;http://jdom.org/&quot;&gt;&lt;b&gt;JDOM&lt;/b&gt;&lt;/a&gt; – это максимально упрощенная open source Java-библиотека для создания,  парсинга, изменения и сериализации XML-документов. В отличие от других  подобных DOM APIs, JDOM ориентированна на язык Java, а не на  спецификации XML, что очень упрощает код и облегчает его интуитивное  понимание. При этом JDOM не является &quot;велосипедом&quot; и не содержит в себе свой собственный парсер, а использует уже существующие наработки – стандартные пакеты &lt;b&gt;javax.xml&lt;/b&gt; и &lt;b&gt;org.xml.sax&lt;/b&gt;. Если вам понадобилось работать с XML, то обратите сначала  внимание именно на эту библиотеку, скорее всего вам она понравится.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Краткое описание модели библиотеки JDOM&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Так же, как и сам XML, JDOM APIs являются древовидными. Для представления XML в библиотеке, главным образом, используются классы &lt;b&gt;Document&lt;/b&gt;, &lt;b&gt;Element&lt;/b&gt; и &lt;b&gt;Attribute&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
Документ  XML в JDOM представляет из себя дерево, корнем которого является объект класса Document. Экземпляр Document является контейнером для всех остальных элементов и обязательно  содержит один корневой элемент (экземпляр Element). Главной характеристикой объекта Element является его содержание  (Content) и список атрибутов (Attribute). Класс Content является  родительским для классов Element, Comment и Text (а так же для некоторых других), поэтому экземпляры  перечисленных классов могут быть содержимым объекта Element. Объект Element есть ничто иное как XML-тег, у которого обязательно должно быть имя и могут быть атрибуты. Классы Comment и Text соответственно представляют комментарий и текст внутри тега. Экземпляры Attribute, Comment и Text не могут содержать внутри себя никаких элементов и по сути являются объектами, которые содержат только текстовые значения. Значение экземпляра Attribute может быть получено по имени в отличие от Comment и Text, у которых имени нет.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Для того что бы все стало совсем понятно, приведу схему документа, который ниже будет создан и прочитан с помощью JDOM.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHShyphenhypheny0PN6p0K6np3erNNuzXL87BrS9Q4dBDYTsZnGfjuX8xeWcAVYe_6VYzpOcIoBNDZJmwOZDqLIpoN6jGQ-5BmTSk9ds5iRi-pbVQbCF97Qw08eXApnhjZaf4vDD6PyD4Otdjo3LuK-/s1600/jdom-xml-scheme.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;408&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHShyphenhypheny0PN6p0K6np3erNNuzXL87BrS9Q4dBDYTsZnGfjuX8xeWcAVYe_6VYzpOcIoBNDZJmwOZDqLIpoN6jGQ-5BmTSk9ds5iRi-pbVQbCF97Qw08eXApnhjZaf4vDD6PyD4Otdjo3LuK-/s640/jdom-xml-scheme.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;br /&gt;
Представленный документ является списком глав подразделений компании. Документ для простоты содержит всего два руководителя (&lt;b&gt;&amp;lt;head&amp;gt;&lt;/b&gt;). У каждого элемента &lt;b&gt;&amp;lt;head&amp;gt;&lt;/b&gt;  есть атрибут &lt;b&gt;id&lt;/b&gt;, а в качестве содержания – комментарий, кратко описывающий руководителя и элементы &lt;b&gt;&amp;lt;name&amp;gt;&lt;/b&gt; и &lt;b&gt;&amp;lt;department&amp;gt;&lt;/b&gt;, которые в свою очередь содержат текст, представляющий имя руководителя и название подразделения соответственно.&lt;br /&gt;
&lt;br /&gt;
Теперь посмотрим, как будет выглядеть сам XML.&lt;br /&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;Head_list&amp;gt;
  &amp;lt;head id=&quot;1&quot;&amp;gt;
    &amp;lt;!--Head of IT department--&amp;gt;
    &amp;lt;name&amp;gt;Petrov P.&amp;lt;/name&amp;gt;
    &amp;lt;department&amp;gt;IT&amp;lt;/department&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;head id=&quot;2&quot;&amp;gt;
    &amp;lt;!--Head of Sales--&amp;gt;
    &amp;lt;name&amp;gt;Sidorov S.&amp;lt;/name&amp;gt;
    &amp;lt;department&amp;gt;Sales&amp;lt;/department&amp;gt;
  &amp;lt;/head&amp;gt;
&amp;lt;/Head_list&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Создание XML-документа&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Для того чтобы создать XML-документ в общем случае необходимо выполнить следующие этапы:&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Создать экземпляр &lt;b&gt;Document&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Создать корневой &lt;b&gt;Element&lt;/b&gt; и добавить его в экземпляр Document&lt;/li&gt;
&lt;li&gt;Создать и заполнить, начиная с корневого элемента, необходимое дерево с помощью экземпляров &lt;b&gt;Element&lt;/b&gt;, &lt;b&gt;Attribute&lt;/b&gt;, &lt;b&gt;Comment&lt;/b&gt; и т.д.&lt;/li&gt;
&lt;li&gt;Если необходимо, вывести (например в файл) созданный документ как поток байт с помощью класса &lt;b&gt;XMLOutputter&lt;/b&gt;.&lt;/li&gt;
&lt;/ol&gt;Пример метода, который создаёт описанное выше XML-дерево и выводит его на стандартный вывод, а так же в файл Heads.xml.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;private static void createXMLDocument(){
      
        // Создаем документ
        Document xmlDoc = new Document();
        // Создаем корневой элемент
        Element root = new Element(&quot;Head_list&quot;);
        // Добавляем корневой элемент в документ
        xmlDoc.setRootElement(root);
        
        // Создаем элемент head и добавляем ему атрибут
        Element head1 = new Element(&quot;head&quot;);
        head1.setAttribute(&quot;id&quot;, &quot;1&quot;);
        // Добавляем комментарий в контент элемента head
        head1.addContent(new Comment(&quot;Head of IT department&quot;));

        // Создаем элемент name и добавляем в него текст
        Element name1 = new Element(&quot;name&quot;);
        name1.addContent(&quot;Petrov P.&quot;);
          
        // Добавляем элемент name в элемент head
        head1.addContent(name1);
          
        // Создаем элемент department
        Element department1 = new Element(&quot;department&quot;);
        department1.addContent(&quot;IT&quot;);
        head1.addContent(department1);
          
        // Добавляем элемент head в корневой элемент
        root.addContent(head1);
      
        // Еще один элемент head создадим более кратко
        root.addContent(new Element(&quot;head&quot;).setAttribute(&quot;id&quot;,&quot;2&quot;)
                        .addContent(new Comment(&quot;Head of Sales&quot;))
                            .addContent(new Element(&quot;name&quot;).addContent(&quot;Sidorov S.&quot;))
                            .addContent(new Element(&quot;department&quot;).addContent(&quot;Sales&quot;))
                         );
       
        try {
            // Получаем &quot;красивый&quot; формат для вывода XML
            // с переводами на новую строку и отступами
            Format fmt = Format.getPrettyFormat();  

            // Выводим созданный XML как поток байт на стандартный
            // вывод и в файл, используя подготовленный формат
            XMLOutputter serializer = new XMLOutputter(fmt);
            serializer.output(xmlDoc, System.out);
            serializer.output(xmlDoc, new FileOutputStream(new File(&quot;Heads.xml&quot;)));
        }
        catch (IOException e) {
            System.err.println(e);
        }
  }
&lt;/pre&gt;&lt;br /&gt;
Из комментариев весь код должен быть понятен, но все же сделаю пару пояснений.&lt;br /&gt;
&lt;br /&gt;
Метод &lt;b&gt;addContent()&lt;/b&gt; после выполнения возвращает ссылку на тот же объект из которого он был вызван, благодаря чему можно создавать целую цепочку вызовов &lt;b&gt;addContent()&lt;/b&gt;, заполняя содержание всех элементов очень небольшим объемом кода. Однако, не стоит этим сильно увлекаться, что бы код оставался понятным.&lt;br /&gt;
&lt;br /&gt;
Форматом вывода можно управлять с помощью класса &lt;b&gt;org.jdom.output.Format&lt;/b&gt;. Например, можно установить отступ, символ новой строки, кодировку вывода, указать как следует выводить пустые элементы (&amp;lt;empty/&amp;gt; или &amp;lt;empty&amp;gt;&amp;lt;/empty&amp;gt;), установить автоматическое удаление лишних пробельных символов и т.д..&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Разбор XML-файла&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Работа с существующим XML-файлом состоит из следующих этапов:&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Создание экземпляра класса &lt;b&gt;org.jdom.input.SAXBuilder&lt;/b&gt;, который умеет строить JDOM-дерево из файлов, потоков, URL и т.д.&lt;/li&gt;
&lt;li&gt;Вызов метода &lt;b&gt;build()&lt;/b&gt; экземпляра SAXBuilder с указанием файла или другого источника.&lt;/li&gt;
&lt;li&gt;Навигация по дереву и манипулирование элементами, если это необходимо.&lt;/li&gt;
&lt;/ol&gt;С первыми двумя пунктами все предельно ясно, а вот навигация по дереву может осуществляться множеством способов.&lt;br /&gt;
&lt;br /&gt;
Для того, чтобы пробежаться по всем элементам дерева (экземпляры Element), можно воспользоваться методом &lt;b&gt;getChildren()&lt;/b&gt;, что бы получить список List всех дочерних экземпляров корневого Element, а затем для каждого дочернего элемента так же вызвать getChildren() и т.д.&lt;br /&gt;
&lt;br /&gt;
Пример использования getChildren()&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public static void listChildren(Element element) {
    System.out.println(element.getName());
    List children = element.getChildren();
    Iterator iterator = children.iterator();
    while (iterator.hasNext()) {
      Element child = (Element) iterator.next();
      listChildren(child);
    } 
}
&lt;/pre&gt;&lt;br /&gt;
Можно поступить по-другому – использовать методы &lt;b&gt;getContent()&lt;/b&gt; и &lt;b&gt;getDescendants()&lt;/b&gt;, передавая им фильтры для того что бы ограничить выборку.&lt;br /&gt;
&lt;br /&gt;
Метод getContent() возвращает список List вложенных элементов, причем поиск осуществляется только на глубине 1 уровня. Таким образом, с помощью getContent() можно получить дочерние элементы объекта у которого вызывается данный метод, но нельзя получить дочерние элементы его дочерних элементов.&lt;br /&gt;
&lt;br /&gt;
Метод getDescendants() возвращает Iterator по всем найденным элементам, но в отличие от getContent() поиск ведется на всех уровнях вложенности элементов.&lt;br /&gt;
&lt;br /&gt;
Как я уже сказал, для ограничения выборки дочерних элементов в обоих методах можно использовать фильтры. Фильтры это классы, которые реализуют интерфейс &lt;b&gt;org.jdom.filter.Filter&lt;/b&gt;. В библиотеке JDOM есть два готовых класса фильтра, реализующих этот интерфейс: &lt;b&gt;org.jdom.filter.ContentFilter&lt;/b&gt; и &lt;b&gt;org.jdom.filter.ElementFilter&lt;/b&gt;. Используя фильтр ElementFilter, можно осуществлять поиск объектов Element ограничивая его именем элемента и/или пространством имен (Namespace), а с помощью ContentFilter можно ограничивать поиск определенным &quot;видом&quot; элемента, например, получить все комментарии и текст, которые содержит элемент или только текст и в таком духе. В тоже время интерфейс Filter очень прост и требует реализации лишь одного метода &lt;b&gt;matches(java.lang.Object obj)&lt;/b&gt;, который возвращает true в случае если элемент obj соответствует заданным параметрам фильтра, поэтому довольно легко можно создавать свои изощеренные фильтры.&lt;br /&gt;
&lt;br /&gt;
Теперь приведу пример метода, который парсит созданный ранее XML-файл. &lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;private static void readXMLDocument() {
      SAXBuilder parser = new SAXBuilder();
      Document xmlDoc;
      
      try {
        xmlDoc = parser.build(new File(&quot;Heads.xml&quot;));

        System.out.println(&quot;Heads:&quot;);
        
        // Получаем список всех элементов head, которые 
        // содержит корневой элемент 
        List elements = xmlDoc.getRootElement()
                            .getContent(new ElementFilter(&quot;head&quot;));      
        
        // Для каждого элемента head получаем значение атрибута
        // id и текст вложенных элементов name и department
        Iterator iterator = elements.iterator();
        while(iterator.hasNext()){          
            Element head = (Element)iterator.next();
            String id = head.getAttributeValue(&quot;id&quot;);           
            String name = head.getChildText(&quot;name&quot;);
            String department = head.getChildText(&quot;department&quot;);
         
            System.out.println(id+&quot;: &quot;+name+&quot; - &quot;+department);
        }
        
        System.out.println(&quot;Comments:&quot;);
        
        // Получаем все комментарии в документе и выводим для 
        // каждого его значение и имя элемента, который содержит
        // этот комментарий
        iterator = xmlDoc.getDescendants(new ContentFilter(ContentFilter.COMMENT));
        while(iterator.hasNext()){    
            Content comment = (Content)iterator.next();
            System.out.println(comment.getParentElement().getName()+&quot;: &quot;+ comment.getValue());
        }
      } catch (JDOMException e) {
        e.printStackTrace();
      } catch (IOException e) {
        e.printStackTrace();
      }
  }
&lt;/pre&gt;После выполнения метода на консоль будет выведено следующее:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;Heads:&lt;/div&gt;&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;1: Петров - IT&lt;/div&gt;&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;2: Sidorov S. - Sales&lt;/div&gt;&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;Comments:&lt;/div&gt;&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;head: Head of IT department&lt;/div&gt;&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;head: Head of Sales&lt;/div&gt;&lt;br /&gt;
Изменение дерева осуществляется с помощью различных методов вроде removeChild(), removeChildren(), removeContent(), addContent(), setContent(), removeAttribute(), setAttribute(), setName(), setText() и т.д. и т.п., думаю что это и так понятно.&lt;br /&gt;
&lt;br /&gt;
Более подробную информацию о JDOM можно найти на &lt;a href=&quot;http://jdom.org/downloads/docs.html&quot;&gt;официальном сайте&lt;/a&gt;, а так же в главах &lt;a href=&quot;http://www.cafeconleche.org/books/xmljava/chapters/ch14.html&quot;&gt;14&lt;/a&gt; и &lt;a href=&quot;http://www.cafeconleche.org/books/xmljava/chapters/ch15.html&quot;&gt;15&lt;/a&gt; свободно доступной &lt;a href=&quot;http://www.cafeconleche.org/books/xmljava/&quot;&gt;книги о работе с XML на Java&lt;/a&gt; (всё на английском). &lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://eqlbin.blogspot.com/feeds/4676525671341156671/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://eqlbin.blogspot.com/2011/03/jdom-xml-java.html#comment-form' title='Комментарии: 7'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/4676525671341156671'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6847658947227037305/posts/default/4676525671341156671'/><link rel='alternate' type='text/html' href='https://eqlbin.blogspot.com/2011/03/jdom-xml-java.html' title='JDOM: работа с XML на Java - проще простого'/><author><name>eqlbin</name><uri>http://www.blogger.com/profile/14670725567348648536</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLnwhMZn6pbiI96winQoDDpihcVlIO7KIFd2p568HxGf2QMMiW8_KT-bqdsYhMQIZ9UsfazfFgPsXHhyMc22g582SIOrDeeLMJR1xI0kaLDqanp_aERhxurXeRwaGXog0/s113/P1040128_6_600.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHShyphenhypheny0PN6p0K6np3erNNuzXL87BrS9Q4dBDYTsZnGfjuX8xeWcAVYe_6VYzpOcIoBNDZJmwOZDqLIpoN6jGQ-5BmTSk9ds5iRi-pbVQbCF97Qw08eXApnhjZaf4vDD6PyD4Otdjo3LuK-/s72-c/jdom-xml-scheme.png" height="72" width="72"/><thr:total>7</thr:total></entry></feed>