Рубрика: Java

о программировании на JAVA

  • Java + Excel файлы (Apache POI)


    С периодичностью раз в два-три года сталкиваюсь с проектами, в которых опять нужно работать с Excel файлами.
    Наверно это у меня что-то кармическое…
    Сейчас, в очередной раз приходиться реализовать полуавтоматический разбор сотен excel-файлов с прайсами.
    В прошлых своих проектах использовал JExcelApi.
    Ничего плохого про эту библиотеку сказать не могу, но хочется какого-то разнообразия в жизни поэтому в этот раз с товарищем решили попробовать Apache POI.

    Apache POI поддерживает как формат для обычных *.xls файлов (HSSF) так и *.xlsx файлы (XSSF).

    Кстати, формат для *.xls файлы в Apache POI они обзывают “HSSF – Horrible SpreadSheet Format”, то есть “Вселяющий ужас формат для электронных таблиц”.
    Для того, чтобы приступить к работе, как обычно прописываем зависимости в pom.xml:

      
            
                org.apache.poi
                poi
                
                3.7
            
        
    

    Для примера, приведу очень простой файл – список ФИО, сумма + Итог:

    Если нужно просто вытащить текст, можно использовать “класс-извелкатель”: ExcelExtractor (далее…)

  • Календари и даты

    Постоянно наталкиваюсь на людей, которые утверждают, что “Date – deprecated, нужно использовать Calendar”.
    Это не верно! Вариаций на тему “почему это не верно” где-только уже не написано, но все-равно появляются люди которые мне доказывают, что правильней использовать Calendar (аргументеруя именно “устареванием” Date).

    На самом деле в классе Date просто много deprecated методов. Но если почитать внимательно документацию, то видно, что устарел не сам класс, а просто часть функций этого класса еще во времена выхода Java 1.1 была вынесена в другой класс. Вот и всё!

    Целевое назначение объектов класса Date сохранилось. Это простой и легкий класс для обозначения времени (например 2010-01-01T12:00:00). Он сериализуется, и если вам нужно хранить именно дату (и время) в каком-то классе, то в большинстве случаев, разумнее всего использовать именно Date.

    Что касается класса Calendar , то это в первую очередь календарик. Он нужен для того, чтобы узнать например какой день недели был на указанную дату, сколько дней прошло с начало года и т.д.. Такой своеобразный аналог обычного настенного календаря с курсором. И гонять его между разными слоями/тирами приложения в составе трансферных объектов или делать его атрибутом какого-то класса для обозначения даты-времени не логично (в большинстве случаев).

    Если вам нужно отформатировать или распарсить дату, вы можете использовать например SimpleDateFormat.

    Кстати о SimpleDateFormat. Многие его тоже используют не правильно. Постоянно наталкиваюсь в чужих исходниках на конструкции типа:

    // так лучше не делать!
    private final static SimpleDateFormat DDMMYYYY = new SimpleDateFormat("dd.MM.yyyy");
    

    Конечно дело не в том, что он статик! Просто SimpleDateFormat он в общем-то не потокобезопасен. Как написано в документации:

    Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

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

    P.S.: Интересно, но использование Calendar вместо Date можно встретить даже в таком матёром API как JCR. Вот не знаю, почему они используют Calendar, а не Date…

  • Apache HTTPD, mod_jk, Apache Tomcat, Linux

    1. Начало.

    Если Вы ставите Tomcat как-то так: (от рута) aptitude install tomcat6 tomcat6-admin tomcat6-examples,
    то прежде чем двигаться дальше, вам возможно стоит обратить внимание на настройки URIEncoding в server.xml.

      
      
    

    Здесь и далее: Томкат => Apache Tomcat, Апач => Apache HTTPD.

    2. Вяжем.

    Классическая схема работы Томката (Apache Tomcat) в продакшене, это связка его с Apache (Apache HTTP Server) через mod_jk. Так делают уже много-много лет и это проверенное на практики и рекомендованное многими решение.
    Связка делается через протокол AJP (Apache JServ Protocol). Это специальный бинарный протокол для прокидки запросов от web-сервера в глубину к другим серверам (например Tomcat-у).

    Зачем такое нужно?
    1. Снижаем нагрузку с томката. Статику (картинки, html-ки и т.д.) пусть лучше отдает апач, а запросы к сервлетами и jsp-кам прокидываются томкату.

    2. Возможна балансировка нагрузки. Например один апач – несколько томкатов.

    Кстати, можно связывать апач не только с томкатом, а например с Jetty (через mod_proxy), и наоборот томкат прикручивать например к nginix. Мы здесь такие варианты не будем рассматривать.

    Итак, допустим у вас убунта с апачем и томкатом. На других линуксовых дистрибутивах аналогично.
    Ставим mod_jk: (от рута) aptitude install libapache2-mod-jk или скачаем и ставим отсюда : http://tomcat.apache.org/download-connectors.cgi

    Из общего правила структуры линуксовой файловой структуры мы знаем, что настройки должны находиться в папке /etc/.
    Легко видеть, что:
    – настройки томката будут в папке /etc/tomcat6
    – настройки mod_jk в /etc/libapache2-mod-jk
    – настройки апача в /etc/apache2

    Внимание! Дальше приводится упрощенная схема для работы в лабораторных условиях.
    На реальных серверах ввод в эксплуатацию без тщательного изучения документации по mod_jk, веб-серверу и томкату крайне опасен!
    Нужно обратить особое внимание на настройки безопасности.

    Теперь приступим. Начнем от фронта.
    В настройках сайта (например: /etc/apache2/sites-available/default) указываем какие запросы куда перенаправлять и кто должен делать обработку таких запросов.
    Например:

     
    ... 
        JkAutoAlias /usr/share/tomcat6-examples/
        JkMount /examples/* ajp13_worker
    
    

    ajp13_worker – название “ажп-работника”, он задается в файле workers.properties (в Убунте: /etc/libapache2-mod-jk/workers.properties):

      # список работников...
      worker.list=ajp13_worker
    

    Также в этом же файле нужно проверить другие параметры (порты, директории и т.д.).
    Если нужна балансировка загрузки, там уже есть заготовка для раунд-робина.

    Где находится файл workers.properties прописано в настройках mod_jk (в /etc/apache2/mods-available файл jk.load)
    Например, что-то типа этого:

    LoadModule jk_module /usr/lib/apache2/modules/mod_jk.so
    
    JkWorkersFile /etc/libapache2-mod-jk/workers.properties
    
    JkLogFile     /var/log/apache2/mod_jk.log
    JkLogLevel    info
    JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
    JkRequestLogFormat     "%w %V %T"
    
    JkOptions +ForwardURICompatUnparsed
    

    На что обратить внимание!
    Параметр JkWorkersFile указывает именно на правильный workers.properties
    Параметр JkOptions имеет значение “+ForwardURICompatUnparsed”, это нужно для нормальной обработки кодировок.

    Значение других параметров следует посмотреть в документации.

    Далее в настройках томката (/etc/tomcat6/server.xml) нужно проверить,
    что НЕ ЗАКОММЕНТИРОВАН AJP Connector (port 8009, URIEncoding=”UTF-8″):

    
     
       
    
    

    Общая схема конфигурационных файлов выглядит так:

    Не смотря на то, что в документации все хорошо описано, и есть такие полезные параметры как “JkAutoAlias”, которые сильно упрощают жизнь.
    Рекомендую, после того как завершите настройку тщательно проверить надежность и безопасность вашей конфигурации.

    По-крайне мере нужно проверить правильно ли будут работать ваши security-constraint и различные фильтры.
    Также проверить ограничение доступа к служебным ресурсам, работу в разных режимах (отключен томкат, реальные запросы из внешней сети и т.д.).

  • Java one, Day #2

    Java One, День Второй. Москва.

    По просьбе друзей выкладываю фотографии с конференции Java One. Фотки не мои, так что вопросы где взять исходные фотографии не ко мне.

    Как я туда попал. Вначале идти не очень хотелось – дел по работе выше крыши. Но поскольку Java One мероприятия последнее время проводились в Питере, то я их постоянно пропускал, в итоге стало уж больно любопытно посмотреть что изменилось… Плюс  товарищ со старой работы сказал, что тоже пойдет, в общем в итоге любопытство взяло вверх.

    Что изменилось?

    На первое что обратил внимание – платный вход. Тарифы были такие:
    Super Saver с 24 февраля по 20 марта, 2011: 500 руб
    Early Bird с 21 марта по 31 марта, 2011 : 1000 руб
    с 1 апреля по 11 апреля, 2011: 1500 руб
    (правда для студентов потом открыли бесплатную регистрацию, но я уже давно не студент )

    Само объявление о Java One увидел в твиттере, тогда еще был тариф Super Saver, так что прошел по минимальному прайсу. Правда с системой оплаты были какие-то глючки, но в итоге я свой билет получил.
    Конференция проходила в здании Академии Наук, м. Ленинский проспект. Такое интересное здание с часиками.

    По ходу были расставлены указатели куда идти.

    На входе дежурили полицейские и проверяли сумки, рюкзаки. На редкость не хамоватые, наверно влияет аура здания Академии Наук.

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

    Т.к. я пришел позже открытия, то к гардеробу очереди уже не было.

    По коридорам ходил Duke. Довольно помятый и запачканный, видать в последнее время жизнь у него была не простая.

    человек-дюк

    Главный зал конференции конечно смотрелся мощно:

    На входе стояли девушки в фирменной одежде и сканировали бейджики. Устройство для сканирования смутно видно внизу на фото:

    Когда начался перерыв на кофе всю еду смели моментом. Вода в термосах закончилась почти сразу.

    Еще можно было сходить на мастер классы. Тоже записывала девушка с красным платочком.

    В этом помещении мастер-классы. Ноутбуки кажется DELL-ы.

    Обед был в пластиковых наборах. В первый день некоторым еда не досталась, а на второй день видел как один парень съел две порции… Вот такой “культур-мультур”, кто-то ходит голодный, кто-то хомячит за двоих )

    Зато были организованы такие приятные места с красными пуфиками,  где был WiFi и можно было посидеть с ноутбуком.

    Из чисто оракловых фишек, был занятный докладчик с очень красивым английским. Рассказывал про то как можно апгрейдить на живую оракловую БД (используя силу фичи – Edition). Грубо говоря как это делают сейчас у нас – когда наступает день Икс, отрубают всех пользователей и гонят ДБА-шников с программистами на работу на выходных (или в ночь), чтобы выложили новую версию в продакшн. Так вот, чтобы всем жилось легче, иногда ЭТО можно с помощью хитрых манипуляций делать постепенно и на живую. Главное знать когда это применимо и как это делать. Вот фото докладчика:

    Ну и конечно же самое главное это всегда не сами доклады… В общем-то я считаю вся соль таких тусовок это возможность пообщаться с другими программерами. Посплетничать о тенденциях,  посмотреть в глаза разработчикам API, помучить разные девайсы.

  • Уменьшение изображения на Java

    Довольно рутинная операция. Есть несколько способов.
    Возьмём исходное изображение:

    1. Самый простой.

    Первый на stackoverflow.
    Суть метода очень проста – создаем BufferedImage меньшего размера, затем прорисовываем в него исходное изображение.
    Далее сохраняем обычным ImageIO.write()

            BufferedImage scaled = new BufferedImage(scaledWidth, scaledHeight,
                    BufferedImage.TYPE_INT_RGB);
            Graphics2D g = scaled.createGraphics();
            g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null);
            g.dispose();
    
            ImageIO.write(scaled, "JPEG", new File("1.jpg"));

    Результат:

    Недостатки:
    – Алгоритм сжатия хоть и быстрый, но результат неприятный.
    – Реализация сохранения JPEG ImageIO по-умолчанию не радует.

    2. Улучшаем качество JPEG-картинки.

    Для этого делаем так:

       /* код приведён только в учебных целях! не использовать в продакшн */
       public static void saveAsJPEG(BufferedImage scaledImage, OutputStream out) throws IOException {
    
            ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(scaledImage);
            Iterator iter = ImageIO.getImageWriters(type, "JPEG");
            // берем первый попавшийся для JPEG
            ImageWriter writer = (ImageWriter) iter.next();
            ImageOutputStream ios = ImageIO.createImageOutputStream(out);
            writer.setOutput(ios);
    
            JPEGImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault());
            iwparam.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT);
            // Вот здесь выкручиваем качество на максимум!
            iwparam.setCompressionQuality(1.0F);
            IIOImage iioimage = new IIOImage(scaledImage, null, null);
            writer.write(null, iioimage, iwparam);
            ios.flush();
            writer.dispose();
            ios.close();
        }

    Результат:

    Недостатки:
    – Сильно вырастает размер файла. Это известный баг. Подробности здесь: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5028259.
    – Алгоритм уменьшения картинки всё равно оставляет желать лучшего.

    3. Используем хинты.

    У Graphics2D есть так называемые RenderingHints, которые позволяют улучшить качество относительно настроек по умолчанию:

    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
    RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    g.setRenderingHint(RenderingHints.KEY_RENDERING,
    RenderingHints.VALUE_RENDER_QUALITY);
    

    Результат:

    Недостатки:
    Качество стало лучше, но не сильно.

    4. Уменьшаем постепенно.

    Суть алгоритма – сжимать не сразу, а в несколько итераций.
    Код можно посмотреть здесь: http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html (очень позновательная и интересная статья, подробно расписано про уменьшение размера картинок).

    Результат:

    5. Метод из JDK 1.1.

    Впринципе также можно воспользоваться методом из JDK 1.1:
    Image.getScaledInstance(width,height, Image.SCALE_SMOOTH)

    Image scaled = originalImage.getScaledInstance(scaledWidth,
                    scaledHeight, Image.SCALE_SMOOTH);
    g.drawImage(scaled, 0, 0, null);
    g.dispose();

    Результат:

    Недостатки:
    Реализация метода может различаться на конкретных платформах.

  • Удобный небезопасный кастинг

    Удобный автоматический кастинг

    Вещь не новая, но для тех, кто начинал программировать на Java в 90-ые и не очень привык к Generics-ам, это может быть интересно.

    Пример использования. Вам нужен метод, который возвращает объекты разных классов. Это могут быть обычные объекты – String, Integer, List, а могут быть и какие-то “свои” сложные бизнес-объекты вроде – ABController, XYZComponent, QWERTYHelper, FooManager (название зависит от возраста и предпочтений программиста).

    Так писали некоторые до Java 5.

        Helper helper = ....; // Какой-то вспомогательный класс, который хранит всякую ерунду
    
        String  path = (String) helper.get("storage.path");  // да, я знаю, хардкодить константы плохо. 
        Integer maxSize  = (Integer) helper.get("max.size"); 
        MyAwesomeDocumentManager manager = (MyAwesomeDocumentManager) helper.get("manager");

    Сейчас можно писать попроще. Конечно это сахар, но код выглядит полегче:
    (далее…)

  • XMPP (Jabber) + Smack

    Недавно с товарищем начал работу над одним проектом, в котором нужно было написать программу для отправки сообщений пользователям по протоколу XMPP (т.е. через jabber).

    Список библиотек для работы нашли на официальном сайте xmpp.org.
    После серии небольших испытаний решили для интеграции с серверной частью написанной на Java использовать библиотеку Smack. Не факт, что будем использовать ее в продакшн, пытаемся распробовать. Пока продукт внушает доверие.

    1. Подключается к проекту элементарно (т.к. есть maven-овском репозитории): (далее…)

  • Работа с видео и аудио в Java. Xuggle

    Первый раз мне пришлось столкнуться с обработкой видео на Java лет 10-12 назад. Я еще был студентом и работал над проектом, в котором мы пытались сделать видео проигрыватель в виде апплета.
    В те времена, для перекодирования видео мы пробовали использовать JMF (Java Media Framework). Нам нужно было сделать хитрую предварительную обработку видео, т.к. показ видео осуществлялся апплетом с помощью самописного алгоритма. В итоге пришлось отказаться от JMF в пользу приложения написанного на С++, т.к. JMF оказался не особо функциональным…

    С тех пор прошло много времени, я участвовал в различных более масштабных и менее сумасшедших (и наоборот) Java-проектах (никак не связанных с видео), а тот проект проект был благополучно сдан и забыт. При этом какие-то знания по способам хранения и сжатия мультимедийной информации оказалось прочно засели у меня в голове (и даже некоторое время мучили ночными кошмарами)…


    Говорят история развивается по спирали. В прошлом году взялся за проект, в котором опять-таки требуется работа с видео/аудио потоками. Небольшие исследования показали, что JMF с тех пор особо не прогрессировал. Нельзя сказать, что этот фреймворк стоял на месте, но прогресс шел явно медленней, чем развитие технологий работы с видео в целом.
    К счастью спасение пришло от проекта (далее…)

  • Быстрый обратный корень на Java

    Fast Inverse Square Root, он же Fast InvSqrt() или 0x5f3759d5 – способ вычисления обратного квадратного корня из числа = 1/sqrt(x). Изобретен еще в 90-х годах.
    Если верить википедии, то этот алгоритм использовался в Quake III.
    Главная фишка метода в использовании “магической” константы – 0x5f3759d5, с помощью которой можно получить первое приближенное значение.
    Затем обычным методом Ньютона получаем улучшенное приближение.
    Подробнее можно прочитать здесь и здесь.

    public static float invSqrt(float x) {
        float xhalf = 0.5f*x; // x пополам
        int i = Float.floatToIntBits(x); // битовое представление числа. 
        i = 0x5f3759d5 - (i >> 1); //  отрицательные x не волнуют, т.к. из отриц. корень брать нельзя
        x = Float.intBitsToFloat(i); // вот оно, первое прибл. значение
        x = x*(1.5f - xhalf*x*x);
         // x = x*(1.5f - xhalf*x*x);
        return x;
    }
    
  • printf

    Небольшой справочник “для себя” по работе с джавным printf().

    Для простоты текста заменил System.out.printf() на printf().

    Базовые конструкции.

    %s → String или toString().

    printf("Hello %s!", "World"); //  "Hello World!"
    

    %n → Перенос строки.

    Byte, Short, Int, Long.

    %d → В десятеричном.
    %x → В шестнадцатеричном.

    %7d → В десятеричном. Минимальная ширина строки 7 знаков.

    printf("%7d", 1); //  "      1"

    %07d → Минимальная ширина строки 7 знаков. Начало забить нулями.

    printf("%07d", 1); //  "0000001"

    Float, Double.

    %f → Десятичное  число с точкой.
    %e → Десятичное  число с точкой и экспонентой.

    %.10f → С точностью 10 знаков после запятой.

    printf("%.10f", Math.PI); //  "3,1415926536"

    Date, Calendar.

    %tF → Дата в формате “год-месяц-день”.

    printf("%tF", new Date()); //  "2011-01-27"

    %tT → Время в формате “час:минута:секунда”.

    
    
    printf("%tT", new Date()); //  "22:42:37"