?

Log in

No account? Create an account
About this Journal
Current Month
 1234
567891011
12131415161718
19202122232425
2627282930
15 апр, 2009 @ 12:42 Форум на Tapestry
Tags:
Создал такой вот журнал:
http://tapestry_forum.livejournal.com
Буду в нём публиковать статьи по Tapestry 5, основанные на своём дипломе. Вообще-то на диплом я писал торрент-трекер, но это, я думаю, будет лишним, а вот форумная его часть - в самый раз для того, чтобы разобраться, как что работает. Там есть и интеграция с Hibernate, Spring, и авторизация, и JavaScript, и собственные компоненты.
В первой статье - что и как устанавливать.
About this Entry
хвост
6 дек, 2008 @ 00:15 Окно IE7 открывается и тут же закрывается
Местонахождение: дома
Музыка: World's End Girlfriend
Tags:
Сегодня потребовалось поставить IE7 для тестирования приложения - у заказчика Vista. Поставил IE7 под XP, позубоскалил на чудовищные цвета логотипа и неимоверно длинную установку, требующую ещё и перезагрузки - ну это так, лирика. Проблема в том, что IE не запускается: окно открывается и тут же закрывается. Судя по сообщениям на форумах, проблема такая не у меня одного, и что с ней делать - толком никто не знает. Запуск с пустой страницей, запуск без аддонов - ничего не помогает.

Решение пришло в лице оболочки Maxthon 1.5, старенькой версии, которой я до сих пор иногда пользуюсь, не смотря на то, что она безумно глючит. Тем не менее, новый движок Explorer она подхватила на раз, что и требовалось. Видимо, какая-то проблема с UI.
About this Entry
хвост
7 сент, 2008 @ 01:16 mp3-кодер на java
Tags: , ,
Ура! Только что закончил написание mp3-кодека на Java.

Фактически, конечно, не писал с нуля, а портировал Shine. Это минималистичный mp3-кодер на C, в котором убрано всё, что только возможно, и годен он, пожалуй, для образовательных целей, или же как заготовка для создания собственного кодека с нуля.

В своей версии я добавил возможность кодирования 8-битных файлов и исправил некоторые ошибки. Во-первых, Shine выдаёт мусор при попытке кодирования mono-файла, хотя корректно определяет его формат. Эту ошибку в исходном коде я не локализовал, при портировании она как-то исправилась сама собой.

Во-вторых, из-за того, что один из массивов не обнулялся перед использованием, первый кадр в выходном файле опять же превращался в мусор.

В-третьих, входной буфер начинал заполняться данными, следующими за словом "data". Однако за "data" следует ещё 32-битная длина данных в файле, что не было учтено, и в первый кадр, опять же, попадали 4 байта мусора.
Есть и ещё одна ошибка, исправившаяся при портировании - Shine не дописывает в выходной файл несколько последних кадров.

Качество выходного звука вполне сносное, т.к. ставилась задача кодирования речи. Хотя, возможно, буду развивать и улучшать код дальше.

Теперь к вопросу о скорости. Скорость на уровне - минутный файл с параметрами моно, 16 бит, 44100 Гц кодируется на моём P4 3.2GHz за 23 секунды под Java 5, и почти в два раза быстрее - за 12 секунд (!) - под Java 6. Похоже, JIT-компилятор в шестёрке неплохо оптимизирует код. Для сравнения, Lame 3.96.1 справляется с тем же отрывком за 4 секунды.
About this Entry
хвост
30 апр, 2008 @ 01:42 daily stuff
Местонахождение: дома
Настроение: busybusy
Получил предложение пофиксить один баг в игрушке, которую до этого хакнул. Баг такого свойства: если в игре включен звук, то она иногда (без какой-либо видимой системы) падает с Access Violation по какому-то непонятному адресу. С выключенным звуком всё работает нормально. Не знаю пока, куда копать, да и времени мало, переводы ждут.

***

Терпеть не могу полуграмотный словесный понос Криса Касперски. Особенно эта его рубрика в "Хакере" про психологию, полнейшая графомания. Ради интереса прочитал статью из апрельского целиком. Кроме отсылок к "Трассе 60" и Пелевину, в этой статье присутствует парочка примеров, демонстрирующих инерцию сознания, и пространные рассуждения о том, что подсознание - штука сложная, и ежели научиться им управлять, то можно ой чего достичь, а ежели, к примеру, человека не кормить, не поить и не лечить, то он, эта, будет, значить, несчастлив и даже, может, помрёт. Обилие словесного поноса, к сожалению, вообще свойственно "Хакеру".
About this Entry
хвост
29 апр, 2008 @ 10:57 (без темы)
Вчера кучу времени убил на дурацкую ошибку (как обычно и бывает). В общей библиотеке класс, который отвечал за работу со спринговским WebApplicationContext, был весь насквозь статическим, включая сам объект контекста. В итоге получилось, что несколько приложений, одновременно работающих на сервере, все свои бины загружают в один и тот же контекст (раньше ошибка не обнаружилась, так как тестировал приложения по одному). Большая часть бинов в приложениях представляет собой по сути одинаковые классы, но лежащие в разных пакетах (это уже легаси, блин! знаю, что параллельные иерархии - это плохо, давно уже надо разрулить). Spring молчит, хоть бы слово сказал, так нет же.
Это привело к тому, что все бины перемешались, и нормально работало только одно из приложений, успевшее при загрузке "раньше встать" и завладеть определёнными "тапками". Другое постоянно валилось с ClassCastException.
Мораль: надо быть поаккуратнее со статическими классами, и с синглтонами, кстати, тоже.

Купил книжку "Ruby on Rails: Быстрая веб-разработка". Перевод книги Тейта, выпущенной в издательстве O'Reilly. Хорошая книжка, но перевод не очень - как в плане точности технических терминов (ну не нравится мне слово "каркас"!), так и в плане "великий русского язык" (с). Понравилось только, что колофон начали переводить, раньше просто не было.
В любом случае, приятно, что книги по Rails потихоньку появляются.
About this Entry
хвост
7 фев, 2008 @ 11:05 wxRuby
Пытаясь установить wxRuby и запустить простейшую программу, входящую в комплект примеров - minimal.rb, - столкнулся с такой ошибкой: ruby ни в какую не хотел находить библиотеку wx и выдавал сообщение:

minimal.rb:10:in `load': no such file to load -- wx (LoadError)
from minimal.rb:10

На всевозможных форумах в данной ситуации рекомендовали запускать ruby с параметром -rubygems или включать эту библиотеку в require. Однако и это не помогало в моём случае.

Проблема решилась заменой rubygems с версии 0.9.5 на последнюю - 1.0.1. Возможно, какая-то ошибка в rubygems, или просто помог сам факт переустановки.
About this Entry
хвост
9 дек, 2007 @ 05:00 SCJP 6 (продолжение). Интерфейсы NavigableSet и NavigableMap
Эти интерфейсы, объявленные впервые в Java SE 6, являются расширениями интерфейсов SortedSet и SortedMap соответственно и добавляют возможность перемещения, "навигации" по отсортированной коллекции. В качестве примера реализации можно рассмотреть классы TreeSet и TreeMap, которые были модернизированы для поддержки этих интерфейсов.

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

E lower(E e);
E floor(E e);
E higher(E e);
E ceiling(E e);


Два метода возвращают соответственно первый и последний элементы, удаляя их из набора.

E pollFirst();
E pollLast();


Следующие методы возвращают итераторы коллекции в порядке возрастания и убывания элементов соответственно.

Iterator iterator();
Iterator descendingIterator();

NavigableSet descendingSet();

И, наконец, методы, позволяющие получить подмножество элементов. Параметры fromElement и toElement ограничивают подмножество снизу и сверху, а флаги fromInclusive и toInclusive показывают, нужно ли в результирующий набор включать граничные элементы. headSet возвращает элементы с начала набора до указанного элемента, а tailSet - от указанного элемента до конца набора. Перегруженные методы без логических параметров включают в выходной набор первый элемент интервала, но исключают последний.

NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive);
NavigableSet headSet(E toElement, boolean inclusive);
NavigableSet tailSet(E fromElement, boolean inclusive);
SortedSet subSet(E fromElement, E toElement);
SortedSet headSet(E toElement);
SortedSet tailSet(E fromElement);


Методы интерфейса NavigableMap<K, V>

Методы данного интерфейса соответствуют методам NavigableSet, но позволяют, кроме того, получать как ключи карты отдельно, так и пары "ключ-значение":

Map.Entry<K,V> lowerEntry(K key);
Map.Entry<K,V> floorEntry(K key);
Map.Entry<K,V> higherEntry(K key);
Map.Entry<K,V> ceilingEntry(K key);
K lowerKey(K key);
K floorKey(K key);
K higherKey(K key);
K ceilingKey(K key);


Методы pollFirstEntry и pollLastEntry возвращают соответственно первый и последний элементы карты, удаляя их из коллекции. Методы firstEntry и lastEntry также возвращают соответствующие элементы, но без удаления.

Map.Entry<K,V> pollFirstEntry();
Map.Entry<K,V> pollLastEntry();
Map.Entry<K,V> firstEntry();
Map.Entry<K,V> lastEntry();


Следующий метод возвращает карту, отсортированную в обратном порядке:
NavigableMap<K,V> descendingMap();

Методы, позволяющие получить набор ключей, отсортированных в прямом и обратном порядке соответственно:

NavigableSet navigableKeySet();
NavigableSet descendingKeySet();


И, наконец, методы, позволяющие извлечь из карты подмножество. Как и в случае NavigableSet, указываем в параметрах начальный и конечный элементы массива ключей, а также необходимость включения в выходной набор граничных элементов. Опять же, если не указывать флаги, то будет использован интервал ключевых значений со включённым начальным элементом, но с исключённым конечным элементом.

NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive);
NavigableMap<K,V> headMap(K toKey, boolean inclusive);
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive);
SortedMap<K,V> subMap(K fromKey, K toKey);
SortedMap<K,V> headMap(K toKey);
SortedMap<K,V> tailMap(K fromKey);


About this Entry
хвост
8 дек, 2007 @ 00:48 SCJP 6
Новая версия сертификационного экзамена Sun Sertified Programmer for Java SE 6 (310-065) ещё не объявлена на русскоязычном сайте Sun, однако ваучер уже можно получить, если связаться с российским представительством Sun Educational Services по электропочте.

Длительность обновлённого экзамена увеличена - 210 минут против 175 в предыдущей версии. Возможно, это связано с соответствующим повышением "проходного балла" - минимум 47 правильных ответов из 72 (в "пятёрке" было 43). Если сравнить требования двух экзаменов, то отличия выявляются лишь на уровне стандартного API. В новой версии требуется умение организовать диалог с пользователем с помощью нового класса java.io.Console и знание новых интерфейсов коллекций NavigableSet и NavigableMap - расширений интерфейсов SortedSet и SortedMap соответственно.

Класс java.io.Console позволяет просто и удобно организовать диалог с пользователем в текстовом режиме, а самое главное - вводить пароль без отображения символов, что в предыдущих версиях Java было невозможно без применения нативного кода. Объект Console, связанный с текущей JVM, можно получить вызовом статического метода класса System:

Console console = System.console();

Если с данным экземпляром JVM не связана консоль (например, запущено Swing-приложение), то результатом вызова будет null.

Основные методы класса Console:

Методы получения "читалки" и "писалки" для организации более сложного ввода-вывода, чем предусмотренный построчный:
public PrintWriter writer();
public Reader reader();


Два идентичных метода для форматированного вывода в printf-стиле, позволяют объединять вызовы в цепочки:
public Console format(String fmt, Object... args);
public Console printf(String fmt, Object... args);


Методы для считывания строки (с отображением вводимых символов) и пароля (без отображения):
public String readLine();
public char[] readPassword();


Аналогичные методы, позволяющие предварительно вывести на консоль форматированную строку:
public String readLine(String fmt, Object... args);
public char[] readPassword(String fmt, Object... args);


Класс Console реализует интерфейс Flushable, поэтому имеется метод, выводящий на консоль все буферизованные данные:
public void flush();

Все методы, выполняющие форматированный вывод, выбрасывают исключение java.util.IllegalFormatException, если форматная строка содержит ошибки. Считывающие методы при завершении входного потока возвращают null, а при возникновении ошибки ввода-вывода выбрасывают java.io.IOError. Концевые символы перевода строки обрубаются и в возвращаемую строку не входят.

Небольшой пример применения класса.


import java.io.Console;
import java.io.IOError;
public class ConsoleTest {
    public static void main(String args[]) {
        Console console = System.console();
        if (console == null) {
            System.err.println("Cannot find the console!");
            return;
        }
        try {
            String username = console.readLine("Username:\n");
            char[] password = console.readPassword("Good morning, mister %s! Please enter your password:\n",  username);
            console.printf("Your username is %s and your password is %s! Mwahhaha!\n", username, new String(password));
        } catch (IOError e) {
            System.err.println("Whoops! Something went wrong: " + e.getMessage());
        }
    }
}
About this Entry
хвост
25 ноя, 2007 @ 01:02 Копаюсь в Java SE 6 Update N Early Access.
Местонахождение: дома
Настроение: busybusy
Инсталлятор весит 14,5 МБ (против 13 МБ - JRE 6), блещет новым логотипом и не пугает лицензионным соглашением, которое спрятано под кнопку. В каталоге установки обнаруживается несколько новых exe-шников, которые ничего не выдают и не принимают никаких параметров.
Единственной вменяемой программой из новых является Java Quick Start, расположенная по адресу \bin\jqs.exe. Эта программа, если верить сайту проекта, обеспечивает предварительную загрузку частей рантайма в память. В конфигурационном файле программы \lib\deploy\jqs\jqs.conf, действительно, прописаны модули JRE и некие диапазоны 32-битных адресов, очевидно, те самые предварительно загружаемые части.
Программа представляет собой службу, которая автоматически устанавливается в ходе установки JRE. Однако управлять службой можно и из командной строки, не заглядывая в services.msc. Исполняемый файл jqs.exe принимает следующие ключи:

-disable - выключает сервис, при этом программа остаётся в списке сервисов, но отключается, и тип запуска (Startup Type) меняется на Disabled.
-enable - включает сервис, при этом в списке служб он отображается с параметрами Status: Started, Startup Type: Automatic.
-unregister - удаляет программу из списка служб.
-register - регистрирует службу, но не запускает её. Для обновления списка и появления в нём службы Java Quick Start пришлось перезапускать services.msc, простого обновления по F5 оказалось недостаточно.
Те же действия по добавлению или удалению службы можно выполнить с помощью "Java Control Panel" в панели управления (вкладка Advanced -> Miscellaneous -> Java Quick Starter)

Чтобы протестировать эффективность работы "стартёра", я пробовал запускать jEdit 4.3pre11 со включенной и с выключенной службой Java Quick Start, в обоих случаях - сразу после перезагрузки компьютера. Результат оказался впечатляющим - почти 10 секунд до появления заставки jEdit проходило без "стартёра", а с ним - программа запускалась практически мгновенно.

Ещё одно нововведение - новая свинговая шкурка Nimbus. Полюбоваться на неё можно с помощью того же jEdit. В списке look-and-feel'ов (Utilities -> Global Options -> Appearance) после установки JRE должен появиться новый - Nimbus. Для применения настроек понадобится перезагрузка.
Выглядит действительно очень приятно. Скруглённые углы, градиенты, красивые шрифты, все элементы управления подсвечиваются при наведении указателя мыши - действительно, Metal отдыхает. Но где-то что-то ещё не закончено или не отлажено - раскрывающееся дерево выглядит довольно странно, а окно Help -> About jEdit... вообще не запускается, ругаясь на какие-то процедуры отрисовки шрифтов. Надеюсь, это исправят, потому что в целом новый дизайн интерфейса выглядит ничем не хуже Flex.


About this Entry
хвост
21 окт, 2007 @ 21:15 Шифрование HTTP-контента
Местонахождение: дома
Настроение: accomplishedi guess,i'll repeat the chorus
Музыка: King Crimson - Happy With What You Have To Be Happy With
Tags: ,

Итак, поставлена следующая задача: на уровне протокола HTTP обеспечить шифрование информации между клиентом и сервером, на котором запущено web-приложение. На серверной стороне шифровать контент, генерируемый сервлетами, т.е. всё, что исходит с некоторого заданного URI; на клиентской стороне - шифровать содержимое POST-запроса.

Начнём с серверной стороны. Если верить документации, механизм фильтров сервлетов как раз подходит для решения задачи. Однако реальных примеров использования фильтров для этой цели в литературе мне найти не удалось, хотя везде такая возможность упоминается.

Механизм фильтров подразумевает, что приходящий запрос, перед тем, как в виде объекта HttpServletRequest поступить на обработку сервлету, передаётся методу фильтра doFilter. В этом методе мы можем выполнить какие-либо действия над объектом запроса, затем передать его объекту FilterChain – это, фактически, ссылка на следующий фильтр в цепочке, либо на сервлет, если все фильтры пройдены. Далее в рамках того же метода doFilter можно обработать полученный объект HttpServletResponse, который по завершении метода отправится обратно к предыдущему фильтру, а если это первый фильтр в цепочке - клиенту.

Цепочку фильтров можно задать в дескрипторе развёртывания web.xml, причём последовательность фильтров в файле будет соответствовать их последовательности в цепочке. Теги описания фильтров похожи на теги описания сервлетов: filter-name задаёт имя фильтра, filter-class – класс, а url-pattern – шаблон для адресов, к которым применяется фильтрация. Если приложение расположено по адресу http://myserver.com/myapplication, то фильтр с указанными далее настройками будет действовать для всех запросов по адресам, начинающимся с http://myserver.com/myapplication/app. Кстати, эти адреса совсем необязательно должны быть отображены на сервлеты. Таким образом, с помощью механизма фильтров можно обрабатывать и запросы к статическому контенту, например, рисункам, таблицам стилей или файлам JavaScript.

 

    <filter>

        <filter-name>CryptFilter</filter-name>

        <filter-class>CryptFilter</filter-class>

    </filter>

 

    <filter-mapping>

        <filter-name>somepackage.CryptFilter</filter-name>

        <url-pattern>/app/*</url-pattern>

    </filter-mapping>

                

В интерфейсе Filter определены три метода. Методы init и destroy по своему назначению подобны одноимённым методам сервлетов, то есть служат для инициализации и финализации фильтра. Рассмотрим содержимое метода doFilter.

Параметрами метода являются объекты ServletRequest и ServletResponse. Так как мы обрабатываем только HTTP-запросы, можно сразу преобразовать их к соответствующим типам:

HttpServletRequest request = (HttpServletRequest) servletRequest;

HttpServletResponse response = (HttpServletResponse) servletResponse;

Далее создадим обёртки этих объектов, в которых реализуем всю логику шифрации/дешифрации содержимого:        

ResponseWrapper rs = new ResponseWrapper(response);

RequestWrapper rq = new RequestWrapper(request);

Сформированные обёртки передадим далее по цепочке фильтров, в нашем случае они будут направлены сервлету:

filterChain.doFilter(rq, rs);

Полученный ответ зашифруем с помощью метода обёртки getEncoded и выведем в выходной поток объекта response, который и будет передан клиенту:

 PrintWriter pw = response.getWriter();

 pw.write(rs.getEncoded());

 

Рассмотрим теперь объекты-обёртки. В Servlet API имеются специальные классы HttpServletRequestWrapper и HttpServletResponseWrapper, предназначенные для «обёртывания» запроса и ответа, соответственно.

public class ResponseWrapper extends HttpServletResponseWrapper{…}

public class RequestWrapper extends HttpServletRequestWrapper{…}

 

В классе ResponseWrapper создадим внутренний класс ByteArrayServletOutputStream, расширяющий ServletOutputStream. Этот класс мы подсунем сервлету, который, ни о чём не подозревая, запишет туда данные, адресованные клиенту. Затем легко переведём этот поток в байтовый массив с помощью инкапсулированного объекта ByteArrayOutputStream и зашифруем.

private static class ByteArrayServletOutputStream extends ServletOutputStream {

        ByteArrayOutputStream baos;

 

        public ByteArrayServletOutputStream(ByteArrayOutputStream baos) {

            this.baos = baos;

        }

        public void write(int param) throws IOException {

            baos.write(param);

        }

    }

 

Также в классе-обёртке объявим байтовый массив, в который попадёт вывод сервлета, и обёртку над ним:

   private ByteArrayOutputStream baos = new ByteArrayOutputStream();

   private ByteArrayServletOutputStream basos = new ByteArrayServletOutputStream(baos);

 

В обёртке ответа обязательно необходимо переопределить методы getOutputStream и getWriter, которыми сервлет может воспользоваться для вывода в выходной поток:

    public ServletOutputStream getOutputStream() throws IOException {

        return basos;

    }

 

    public PrintWriter getWriter() throws IOException {

        return new PrintWriter(baos);

    }

 

А вот, собственно, и метод, которым мы воспользуемся из фильтра для получения уже шифрованного содержимого ответа. Объект encoder реализует интерфейс кодера с единственным методом encode, получающим массив байт и возвращающим тоже массив, но уже зашифрованный.

    
              String getEncoded() {

        return new String(encoder.encode(baos.toByteArray()));

    }

О реализации объекта RequestWrapper, к слову, чуть более сложной – в следующий раз.

About this Entry
я