Метка: Java

  • Про архитектуру

    Решил написать ещё одну заметку про проектирование больших программных продуктов. Меня периодически спрашивают, как разработать качественную архитектуру. При этом программистов и менеджеров интересуют разные аспекты. Программисты больше фокусируются на технических вопросах (дизайн, стек технологий, язык программирования), а менеджмент — на бизнес-аспектах, например, как выбрать и нанять архитектора, как принятые решения повлияют на стоимость разработки и поддержки проекта.

    Есть такая книга “Thinking, Fast and Slow“. На русский язык название почему-то перевели как “Думай медленно… решай быстро“, хотя буквальный перевод означает “Быстрое и медленное мышление”.

    Автор книги Даниэль Канеман – нобелевский лауреат по экономики, но книга не только про экономику. Я бы сказал, что она написана скорее в стиле занимательной психологии. Читал давно, но после прочтения, хорошо запомнилось несколько интересных идей. Самое интересное в ней про две системы мышления – быстрая и медленная.

    Быстрое мышление хорошо развивается тогда, когда есть быстрый ответ на действие (назову его знакомым для тех специалистов термином – “фидбек”). Например если взять профессию пожарного, то он “чует” опасность, его интуиции можно доверять. Если кричит бежим – надо бежать, кричит лежать – надо падать и лежать. Иначе в этой профессии не выжить, поскольку “фидбек” на действие (или бездействие) срабатывает мгновенно. Причем с возрастом интуиция у них развивается всё больше и больше. Это и есть быстрое мышление. Оно не плохое и не хорошее, оно нужно для того, чтобы экономить силы и время, а иногда даже спасает жизнь.

    С другой стороны, если взять профессию терапевта, там “фидбек” медленный. Пациент может выздороветь из-за правильного лечения, а может сам по себе, за счет крепкого иммунитета. Терапевт может никогда не узнать, сработало его назначения или нет, поскольку пациент может не прийти на повторный прием по нескольким причинам. Например может перестать обращаться к этому врачу если стало лучше (нет смысла дальше тратить время и деньги) или если стало хуже (опять нет смысла, надо менять клинику/врача). Получается в таких профессиях сложнее развивать интуицию, поскольку обратная связь может быть очень долгой или полностью отсутствовать. В итоге доверять можно только такому врачу, который смог пройти за годы работы через огромный массив неточных, зашумленных данных и развить у себя экспертизу в своей профессиональной деятельности.

    К чему я веду.

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

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

    С архитектурой дела обстоят еще хуже. Для того, чтобы разбираться в этом вопросе, нужно выпустить несколько программных продуктов, посмотреть как они ведут себя на рынке, проследить как развиваются и только после этого можно получить хоть какой-то “фидбек”, чтобы в дальнейшем понять насколько были адекватны принятые архитектурные решения. При этом в большинстве случаев, программисты и DevOps-инженеры будут месяцами стоически поддерживать проект с ужасной архитектурой.

    Получается, что если слабого программиста в организации можно “разоблачить” через несколько недель или месяцев, то ошибки архитектора могут проявить себя еще позже, например, через год или два.

    Причем хороший программист не всегда будет нормальным архитектором, он может быть силен в алгоритмах и быть ценным специалистом в своей области, но быть слабым архитектором. Например выдавать настолько сложные решения, что работать с ними могут только специалисты такого же высокого уровня. Это любимая тема для противников набора в команду звездных программистов (“Rockstar Developer”-ов).

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

    • Все успешные компании используют Spring и Hibernate!
    • В проде – только Oracle (хотя сейчас чаще слышу – только PostgreSQL)!
    • Нам нужна Кафка!
    • Все используют микросервисы!
    • Только Java, т.к. C# скоро умрет (здесь вместо Java и С# – можно подставить любые другие языки программирования)!

    Это конечно звучит мощно и просто, но… нет, не надо торопиться. Здесь как раз и надо включать, то самое “медленное мышление” про которое пишет Канеман, так называемая “Вторая система”:

    • Система 1: срабатывает автоматически и очень быстро, почти не требуя усилий и не давая ощущения намеренного контроля
    • Система 2: выделяет внимание, необходимое для сознательных умственных усилий, в том числе для сложных вычислений

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

    Вот несколько рекомендаций с моей стороны.

    • Конечно замечательно, когда можно довериться интуиции. Только интуицию нужно сформировать, а для этого нужен опыт. Причем опыт не программирования, а именно проектирования архитектуры. Проверить программный код можно относительно быстро, также быстро проверить архитектуру – нет.
      Ищите человека с опытом.
    • Если опытного архитектора нет, а есть простой программист, который выполняет эту роль, то возможно его интуиция еще не развита в должной степени. Тогда ему на этапе проектирования архитектуру надо стараться думать, анализировать, искать недостающую информацию, а не действовать интуитивно. Не использовать первую систему, методика “попробуйте выключить и включить” не сработает.
    • На проекте должен быть только один архитектор, но об этом как-нибудь в другой раз…
  • Запечатывание в Java

    Наконец-то нашел время дописать статью в блоге, которая давно уже в черновиках лежит…. Итак, про запечатанные классы и немного про алгебраические типы данных.

    Запечатанные классы определяются с помощью ключевого слова sealed. Это нужно, чтобы ограничить в наследовании. Потенциальных наследников в таком случае нужно указать явно, через запятую после permits.

    Например так:

    public abstract sealed class Shape
        permits Circle, Rectangle, Square { ... }

    На самом деле, основной вопрос который мне задают, не как ограничить наследование, а зачем вообще запрещать или ограничивать наследование? Ведь наследование – один из столпов ООП, такое же как и инкапсуляция и полиморфизм…

    Если посмотреть официальные цели в JEP, то в них написано:

    1. Allow the author of a class or interface to control which code is responsible for implementing it.

    2. Provide a more declarative way than access modifiers to restrict the use of a superclass.

    3. Support future directions in pattern matching by providing a foundation for the exhaustive analysis of patterns.

    JEP 409: Sealed Classes

    Цели №1 и №2 под “контролировать” и “… более декларативный способ для ограничения … ” означает “мы даем вам возможность запрещать”, не раскрывая зачем это нужно. Цель №3 интереснее, в ней виден прикладной аспект, но о нем позднее.

    Начнем с того, что прикладному программисту, который пользуется готовыми библиотеками, особой пользы в sealed нет или эта польза не особо видна. Этот “инструмент для запечатывания” нужен архитектору или ведущему программисту, который пытается построить дизайн/архитектуру проекта.

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

    java.lang.Object
      ↑ org.springframework.core.io.DefaultResourceLoader
        ↑ ...context.support.AbstractApplicationContext
          ↑ ...support.AbstractRefreshableApplicationContext
            ↑ ...AbstractRefreshableConfigApplicationContext
              ↑ ...AbstractRefreshableWebApplicationContext
                 ↑ AnnotationConfigWebApplicationContext

    В случае Spring-а это сделано умышлено, это всё росло годами и хорошо показывает всю гибкость этого огромного фреймворка.

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

    Теперь возможно.

    Если смотреть на вопрос глобально, то ограничения в программирование, это очень полезная вещь. Например некоторые радикально настроенные программисты вообще считают, что ограничение – двигатель прогресса.

    Очень ёмко эту идею изложил Роберт Мартин (также всем известный как дядюшка Боб или “Uncle Bob”) в книге Идеальная архитектура.

    Парадигмы говорят нам не столько что делать, сколько чего делать нельзя.

    Роберт Мартин.

    Здесь речь идет, не об sealed классов, а в целом о роли различных запретов и ограничениях в программировании.

    Например структурное программирование – борется с таким злом как оператор “goto”. ООП вводит ограничение на косвенную передачу управления. Функциональное программирование накладывает ограничение на присваивание.

    Конечно это слишком смелая идея, полностью принять и согласиться с ней я не могу. Например, мне кажется, что крутость ООП не только в том, что борется с передачей управлением, а ещё в том, что борется засильем глобальных переменных, а также дает возможность программистам выразить в коде “вот эти методы и поля не смей трогать!”.

    С другой стороны, в целом идея верная. Например если взять такой живой пример, как очень популярный сейчас язык программирования Python. В нем наложили запрет на плохо отформатированный код. Казалось бы люди должны страдать от того, что не могут ставить пробелы и/или таб так, как они хотят и где хотят. Тем не менее, за счет этого ограничения, Python стало особо привлекателен и выразителен.

    Вернемся к sealed классам.

    Использование запечатанных классов довольно простое.
    Перед классом или интерфейсом указываем sealed, потом указываем после permits список классов-наследников. Классы должны быть final.

    Например:

    sealed class S permits A,B,C { }

    Означает, что объект класса S может быть объектом класса A, B или C.

    S abc = new A(); // Объект abc может быть только A, B или C
    abc = new B();
    abc = new C();

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

    В качестве грубой аналогии, это можно представить как сумму множеств:

    S = тип A + тип B + тип C 
      или можно написать так:
    S = A U B U C 

    Осторожно! Это всего лишь грубая аналогия. Мои выкладки здесь никак не связанны с теорией множеств и настоящей математикой. Кто учил матан и дискретку должны это понимать.

    Теперь двигаемся дальше в сторону алгебраических типов данных. Берем произведение типов. Мы можем их сделать с помощью record.

    Например:

    record R(A a, B b) { }

    Аналогия прямого произведения множества R = A x B, которое может содержать различные комбинации объектов из A и B.
    Естественно объекты класса R также могут быть частью S.

    sealed interface S permits A,B,C,R {}
    
    record R(A a, B b) implements S {}

    Что-то вроде S = A U B U C U R , где R = A x B

    Также в permits можно использовать enum.

    sealed interface S permits A,B,C,R,E {}
    record R(A a, B b) implements S {}
    // Делаем enum для запечатанного S
    enum E implements S { AZ, BUKI, VEDI, GLAGOL, DOBRO }

    Важно понимать, что sealed это про объединение “типов”, а enum – объединение объектов одного типа. Разница как между множеством и элементами множества.

    Архитекторы языка Java нам оставили специальное ключевое слово non-sealed. Это такой аварийный люк, который можно использовать, чтобы “распечатать” запечатанный класс.

    sealed interface S permits A,B,C,R,E, Gin { }
    record R(A a, B b) implements S { }
    enum E implements S { AZ, BUKI, VEDI, GLAGOL, DOBRO; }
    // От этого класса можно наследоваться, он не final
    non-sealed class Gin { }

    Ключевое слово non-sealed очень необычное. Это первый hyphenated keyword. Другими словами написанный через дефис, т.е. в kebab-case или шашлык-нотации. Такая нотация наиболее популярна в lisp-е и в частности в clojure, но в Java встречается впервые.

    Что касается в целом последних изменений в языке и своего опыта. В последнее время активно использую record. По ощущениям, это “новшество” почти такое же крутое, как появление в 2004-ом году generic-ов в Java 5 или нормальных коллекций в 1998-ом, когда вместо Vector и Hashtable в Java 2 появились List, Map.. Постоянно думаешь, почему этого не было раньше.

    Про sealed не могу сказать также. Жду сентября 2023 года, когда выйдет Java 21 LTS. Тогда можно будет использовать новый pattern matching в полную силу.

    Про новый pattern matching в другой раз, а то итак статья очень большая получилась.

    PS: Сделал телеграм канал @prgrmdr для анонсов

  • Про record

    Появились в версии Java 14, но я предпочитаю использовать LTS версии, поэтому начал активно использовать только в Java 17.

    Не вижу смысла в очередной раз переписывать однотипные статьи из интернета с одними и тем же примерами record-ов вида “Hello World”. Здесь хотел поделиться своими впечатлениями после нескольких месяцев использования, когда и как их удобно использовать, а также небольшими техническими нюансами.

    Если кратко, то самое банальное использование record-ов, это применять их вместо кортежей (tuple). В более сложных кейсах – создание объекта для хранения/передачи данных. Причем хранение и передачи не в контексте работы с СУБД или передачи через сеть, а в самом простом смысле – внутри кода.

    Вот простой пример. Представьте, допустим был какой-то метод:

    String findFoo(...)

    Пусть этот метод возвращает какой-то строковый идентификатор (ID).

    Теперь представим, что ситуация изменилась и нужно возвращать не только ID, а например ещё булевый флаг true/false.

    После наступления такого события у некоторых java-программистов (особенно начинающих) могут начаться душевные метания, экзистенциальный кризис. Возникает внутренний конфликт между необходимостью вернуть из метода сразу несколько значений и отсутствием такой возможность в синтаксисе Java. Причем осознание того, что в языках программирования Scala или Python такая возможность есть ещё больше усиливает страдания и начинает порождать мысли о том, что дальнейшее программирование на Java лишено смысла и нужно что-то делать. Потом возникают различные шальные идеи:

    • А что если просто сделать еще один метод, который делает почти тоже самое, но возвращает булевый флаг, например: boolean findFooFlag(..)?
    • А что если сделать мапу/лист/массив и в него положить два значения, сейчас же это просто! Есть Map.of(), List.of() или вообще все преобразовать к String[]?
      Был метод String findFoo(...), станет String[] findFoo(...).
    • А может взять какой-нибудь готовый Pair<String, Boolean>, смысл изобретать велосипед?
      Был метод String findFoo(...), станет Pair<String, Boolean> findFoo(...). На стековерфлоу предлагают кучу библиотек – javatuples, apache commons collections, Map.Entry и т.д.

    Получается вместо того, чтобы сделать простой класс, который содержит пару полей, программист начинает искать разные обходные варианты и избегает создания нового класса. Иногда я даже замечаю, что у некоторых программистов есть особая фобия – боязнь создания нового класса – NewClassPhobia, т.к. для них создание совершенно нового класса, это равносильно выходу из зоны комфорта.

    Сделать REST-контроллер на Spring-е, который смотрит в БД и возвращает данные – это за 5 минут, никаких проблем. Взять готовый код, поправить в нем какие-то хитрые условия, критерии выборки, разобрать бизнес-логику – 10-15 минут и никаких проблем…

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

    Я вижу несколько причин.

    Во-первых лень. Зачем делать, когда можно не делать и взять готовый Pair, Tuple и т.д.?
    Во-вторых нужно придумывать название для класса. Это сложно.
    В-третьих это “много строк кода” – объявить поля, сделать конструктор, сделать get***() методы для каждого поля. Среда разработки конечно делает эту всю работу за программиста, но все равно нужно лишний раз нажимать на кнопки.


    Ситуация становится совсем фатальной, если добавляется еще один объект, например метод должен возвращать ещё какой-нибудь внешний идентификатор, например long extId. В таком случае Pair не проходит, а делать Pair<String, Pair<Boolean, Long>> или подключать Triplet<String, Boolean, Long> уже перебор.

    Так вот тут нам и помогут record-ы.

    Объявляем например:

    record Foo(String id, boolean enabled, long extId) {} 

    Головная боль уходит!

    Правда есть неудобство, если собираем больше 3-х полей в одном рекорде, то начинает пухнуть конструктор. С учетом того, что в java нет именованных параметров, легко ошибиться и перепутать поля, особенно если все четыре поля – строки. Можно попробовать решить это через билдер, но совсем другая история.

    Кроме этого неудобства, в остальном мне очень нравятся рекорды. Их удобно использовать как в императивном стиле, так и в функциональном, особенно удобно для map-инга в стримах.

    В них есть сразу готовые методы для доступа к полям. В нашем случае id(), enabled(), extId(). Есть готовый toString(), hashCode(), equals().
    Благодаря toString() чуть меньше мороки с логами, а готовый hashCode()/equals(), полезная штука в качестве защиты от дурака, например если рекорды складывать в коллекции.

    Еще рекорды удобны если их вложить внутрь класса. Согласно спецификации вложенный рекорд будет неявный static, т.е. не будет прицепом тащить за собой внешний объект. Более того, его можно объявлять локально, т.е. внутри метода.

    Кстати, для public record-ов конструктор будет всегда public. Просто есть любители статичных фабричных методов, которые прячут private-конструкторами возможность создания объекта. Здесь спрятать просто так не получится.

    Есть интересный вид конструктора, называется компактный конструктор.
    Например вот такой:

    record Foo(String id, boolean enabled, long extId) {
        Foo {
            Objects.requireNonNull(id);
       }
    }

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

    Я думаю, что это не будет популярно. Будет как с assert-ами – штука полезная, но мало кто умеет пользоваться. Из моей практики, рекорды это простые объекты, которыми передают данные и компонуют объекты. По крайней мере я только так их и использую.

    В целом по поводу данных, похоже увлечение функциональщиной проходит и отрасль начинает двигаться в сторону data oriented programming (не путать с data driven development и data-oriented design).

    Также с нетерпением жду когда в java появятся а-ля алгебраические типы данных. Сейчас уже через record-классы – мы можем делать подобие умножения, а через sealed-классы – сложение.

    Если из preview режима выведут type pattern matching, то можно начать использовать. По-крайней мере новый instaceof оказался очень удобный.

    До Scala конечно далеко, но таков Java путь.

    PS: У меня слетели все комментарии к блогу, т.к. были через фб, а как подключить новые еще не придумал. Без комментариев конечно грустно. Может придумаю что-то…
    Еще после очередного обновления WP слетело форматирование кода, тоже буду думать, что с этим делать.

  • Multi-map в Java 8

    Мультикарта (multi-map) может пригодиться в реальной работе или на собеседовании. Почему-то в некоторых компаниях при приёме на работу любят давать алгоритмические задачки на её использование (лично я так не поступаю).
    По сути это обычная карта (Map) в которой значением является коллекция (List или Set).
    Сейчас в JCF (Java Collections Framework) нет готового класса для Multimap.

    До выхода Java 8 приходилось логику работы писать вручную.
    Например так.
    Допустим, нам поступают такие данные:

    Moscow=ru
    Omsk=ru
    Tula=ru
    NY=us
    LA=us
    London=uk и т.д.

    Их нужно представить в виде:

    ru=Moscow, Omsk, Tula
    us=NY, LA
    uk=London

    Тогда логику можно реализовать, например, в таком виде: (далее…)

  • Про настройку шрифтов в IDE (Netbeans) под Linux.

    Какой самый лучший шрифт для программиста?
    Каждому своё.
    Одним нравятся округлые очертания, другому наоборот тонкие и острые. Поэтому выбор шрифта — дело вкуса, хотя конечно есть базовые требования.
    Самое главное и очевидное — шрифт должен быть моноширинным, т.е. все символы должны быть одной ширины. Это очень важно для того, чтобы сохранить структуру кода, т.к. иначе будут плыть все отступы и исходный код будет плохо читаться.
    Если человек пришел в программирование с дизайнерским/типографским прошлым, такой шрифт его может раздражать. Моноширинные шрифты не самые приятные для чтения, хотя их используют не только программисты-техногики, но и люди искусства.
    Например, если верить википедии, в западной театральной и кинематографической традиции сценаристы используют шрифт Courier-12. Одна страница такого сценария длится примерно одну минуту. Вроде это какой-то даже у них стандарт де-факто в отрасли.

    Кроме моноширинности, при выборе следует обратить внимание на следующее:
    – Цифра 0 (ноль) должна отличаться от буквы O.
    – Цифра 1 (один) должна отличаться от буквы l (маленькая L).

    Что касается меня, то сейчас я использую шрифт Consolas.

    Специфика Linux.

    На момент написания этой статьи у некоторых пользователей Linux (Ubuntu) есть определенное недовольство качеством отображения шрифтов в IDE.
    Итак, что можно попробовать сделать.

    Во-первых, я ставлю набор микрософтовских шрифтов:

    $sudo apt-get install ttf-mscorefonts-installer
    

    Во-вторых, особо чувствительные к шрифтам могут поставить Infinality.

    В-третьих и самое главное. Это не сильно помогает, т.к. на момент написания этой статьи в Java (точнее в её графической подсистеме AWT/Swing) есть какие-то проблемы с красивым отображением шрифтом в Ubuntu. Другими словами проблема актуальна для Netbeans и Android Studio.

    Единственное нормальное решение я нашел здесь askubuntu.com.
    Итак чуть более подробный пересказ, что нужно делать.
    (далее…)

  • Про org.json парсер

    С json парсерами в Java дела обстоят следующим образом. С одной стороны их существует несколько разновидностей, с другой стороны сложно сделать выбор.

    Хорошо когда только один критерий принятия решения, например скорость работы или размер библиотеки. Сложности начинаются тогда, когда нужно выбрать с учетом нескольких характеристик.
    В этой небольшой заметке я хотел бы кратко описать случаи, когда уместно использование библиотеки org.json.

    Итак, кратко плюсы и минусы.

    Минусы:
    – Далеко не самый быстрый парсер.
    – Нет автоматической сериализации в JavaBean-ы.

    Плюсы:
    + По-умолчанию есть в стандартной поставке Android
    + Простое API

    Занятные факты:
    ~ Первые версии этой библиотеки были написаны Дугласом Крокфордом (англ. Douglas Crockford) — создателем формата JSON.

    Теперь чуть подробнее.

    Наличие в стандартной поставки Android API это действительно плюс, с учетом лимита в Android-е на кол-во методов в 64К (65 536 шт) любая дополнительная библиотека может обернуться лишней головной болью.

    Другими словами, если нет необходимости разбирать большие массивы данных в json и структура данных примитивна, то им вполне можно обойтись. Типичный пример – небольшой конфигурационный файл, в котором всего около десятка различных параметров.

    В случае если объем данных занимает мегабайты, а ручной разбор и мэппинг в бины – утомителен и однотипен, то естественно надо от него уходить в сторону других решений.
    Лично я предпочитаю в таких случаях использовать gson, но это дело вкуса и технической необходимости. Никаких предубеждений против того же jackson-a я не имею.

    В стандартной Java появление легковесного API пока не предвидится. В новостях были сообщения, что JEP 198: Light-Weight JSON API не будут включать в Java 9, аргументируя это тем, что и так много опенсорных json-парсеров.

    Поэтому если все-таки нужен именно “org.json” в стандартной Java, то его можно подключить через мавен.

    	 	 
    
        org.json
        json
        20160810
     
    

    При этом надо понимать, что во-первых в нормальной Java (не Android) нет ограничений в 64К методов, во-вторых нет стандартного json парсера, поэтому использование org.json не столь очевидно.

    Что касается Java EE, в нем есть стандартный javax.json пакет с классами для работы с JSON-ом.

  • Установка и настройка Apache Tomcat под Linux

    Tomcat-logo

    Причины.

    Замысел написать эту статью про установку и настройку, наверное, одного и самых популярных веб-серверов на Java возник уже давно. Одной из причин было желание сделать небольшую заметку “для себя” с подробной инструкцией. Возможно эта статья также пригодится другим java программистам. Пользы для кого-нибудь ещё, например для системных администраторов в ней будет не так много. Скорее всего они просто сделают так: apt-get install tomcat8 и затем потребуют у программиста war-ик для развертывания. Программист же часто хочет чуть большего — например, возможности работать с различными версиями серверов (которых может даже ещё нет в официальном репозитории) или наоборот откатиться к какой-то специфичной версии. Системному администратору такие исследования, как правило, не нужны. По-хорошему, у него должна стоять просто стабильная работающая версия, на которую периодически он будет накатывать обновления и лишний раз на неё не дышать.

    В общем, это статья про то, как программисту установить Apache Tomcat под Linux чтобы “поиграться” с ним, но при этом ничего сильно не сломать.
    Также эта статья может быть полезна в тех случаях, когда начинающий java программист отладив свое веб-приложение Tomcat запущенным на Windows, сталкивается со жгучим желанием развернуть свой сайт на какой-нибудь недорогой VPS-ке с Ubunt-ой.

    Статья может показаться излишне многословной, но мне хотелось рассказать про основные “грабли” и способы их обхода. Могу предположить, что системные администраторы могут быть недовольны тем, что это решение недостаточно системные. Поэтому тем, кто хочет подробно изучить данный вопрос или посвятить свою жизнь профессиональной (т.е. получать за это деньги) настройки линуксовых серверов, тому лучше следует обратиться к соответствующей литературе, обучится этому ремеслу у профессионала и черпать знания у сисадминского сообщества. Здесь же у меня просто блог для java программистов.

    Итак, поехали!

    Подготовка

    Исходные данные.
    Linux. Debian 9. 64bit.

    1. Устанавливаем JDK.
    Почему JDK, а не JRE? По-факту достаточно JRE, но лично мне приятно иметь возможность в случае необходимости по-быстрому скомпилировать программку на java прямо на сервере.
    Вы не поверите, но жизнь такая интересная штука, никогда не угадаешь когда тебе может понадобится скомпилировать и запустить что-то на Java. Лично мне запуск javac из консоли на сервере помогал несколько раз.

    Далее, я предпочитаю ставить Oracle JDK. Собственно OpenJDK тоже неплох и устанавливается гораздо проще (sudo apt-get install default-jdk). Просто я отдаю предпочтение оригинальной Sun/Oracle. Тем не менее, ставить Oracle JDK, OpenJDK или какую-либо другую версию – личное дело каждого. Лично я отношусь к пользователям Open JDK без предубеждения. Более того, сам часто использую версии Open JDK (например Java 9) для того, чтобы ознакомиться с их новыми возможностями.

    Установка Oracle JDK под Windows и Linux сильно отличаются. Под Windows проще установить Oracle JDK проще простого (скачать и запустить), а сборку Open JDK под Windows нужно ещё поискать.
    С Linux-ом всё наоборот. Open JDK как я писал ставится очень просто через apt, с Oracle JDK чуть сложнее.

    В интернете существует совет, что для установки нужно добавить ещё один apt-репозиторий. Я так не делаю. Возможно это лично моя паранойя, но я стараюсь так не делать и делаю установку руками. Особенно если учесть, что установка заключается в том, чтобы скачать и распаковать архив.

    Для этого заходим на сайт загрузки Oracle.

    Выбираем jdk-XYZ-linux-x64.tar.gz файл. Правой кнопкой – сохранить ссылку.

    Далее скачиваем архив:

    $wget --header "Cookie: oraclelicense=accept-securebackup-cookie" [ссылка]
    

    Например так:

    $wget --header "Cookie: oraclelicense=accept-securebackup-cookie" 
        http://download.oracle.com/otn-pub/java/jdk/8u73-b02/jdk-8u73-linux-x64.tar.gz
    

    Специально для параноиков, нужно проверить SHA-1 сумму (поскольку выкачиваем-то по голому http).

    Проверяем:

    $sha256sum jdk-8u73-linux-x64.tar.gz
    

    Смотрим что показалось на экране и сверяем значение, с тем что опубликовано на официальном сайте. (далее…)

  • Про Android TV.

    androidTV_player-entertainment-default

    Небольшая предыстория.

    Так получилось, что мы пишем приложение для Android TV.
    Вообще, обычно я занимаюсь тем, что в программисткой среде принято называть “суровым энтерпрайзом” — веб-службы, высокая нагрузка и прочие Java EE штуки.

    Тем не менее, иногда мы делаем проекты и под мобильные платформы. Как правило это заказная разработка, например мобильные приложения для внутренних нужд компании (приложения для сотрудников организации/диспетчеров/водителей, интерактивные учебные пособия, системы учета/бронирования, системы мониторинга и так далее), которые потом обычно и интегрируют с бэкендом на “суровом энтерпрайзе”. Этими приложениями пользуется персонал компании, а не простые пользователи. В большинстве случаев они даже не публикуются на маркете.

    Естественно из этого есть исключения.
    Итак. Да, мы пишем под Android TV. Не знаю, много ли существует мобильных разработчиков в России, которые что-то делают под эту платформу. Понятное дело, есть крупные компании, которые занимаются производством и распространением телевизионного и медиа контента и им сам Бог велел заниматься созданием приложений под эту платформу. Когда-то давно, я тоже делал мобильное приложение для одного популярного в определенных кругах телеканала, но сейчас не об этом.

    У нас немного другая история. Мы изначально делали обычное приложение (с кнопочкам и картинками), и по началу думали, что оно будет работать на телефонах, планшетах и китайских свистелках-поделках – “тв приставках”. Потом внезапно выяснилось, что платформа Android TV подошла на удивление хорошо для целей проекта.

    Про Android TV.

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

    1. Нормального эмулятора нет (как обычно). Мы используем оригинальный, привезенный нам из забугорья Android Nexus Player. Настоящий суровый американский Nexus Player с плоской вилкой на 110 В. Без намёка на РОСТЕСТ. Без такого устройства нормальная разработка под Android TV была бы затруднительна. Как и все Nexus-ы он неплохо подходит для целей отладки и разработки. Нет проблем с драйверами, подключением по adb и т.д. Это наше основное девелоперское устройство в этом проекте.

    2. Конечно мы проводим бета-тесты еще и на телевизоре Sony. Как выяснилось, это вполне такой обычный телевизор, просто на нем установлен Андроид.
    Естественно кроме телевизора и Nexus-а, перед тем как выложить приложение, нам приходится тестировать приложение на десятках разных телефонах и планшетах (те кто пишет под Андроид знают, что это не гипербола, а суровые будни андроид программиста).
    Так вот, на телевизоре что-либо дебажить очень не удобно, поэтому опять таки основным устройство у нас является Nexus Player.

    3. В какой-то момент Nexus Player внезапно обновился на Marshmallow (Android M) (что в прочем для нексусов вполне типично).
    В итоге у нас в руках появился очень редкий, можно сказать “краснокнижный” зверь. Устройство под Android TV (которых мало) и с 6-ой версией Android-а (которых еще меньше). Это прямо-таки удивительное и непредсказуемое создание. Часто было не понятно, особенности в его поведении были связанны с тем, что это был шестой андроид или потому, что это Android TV.

    Выводы.

    Стоит ли писать под Android TV? Думаю решать нужно исходя из целей проекта. Приведу свои, субъективные размышления на эту тему (актуально на момент публикации этой статьи).

    1. ТВ стали смотреть меньше. Возможно я ошибаюсь, но это общий тренд. Я не уверен, что в будущем вообще будут много смотреть телевизоры.
    2. Играть на Android TV в игры – сомнительное удовольствие. Я конечно не геймер, но мне кажется, что уж лучше подключить XBox.
    3. Платформа требует отдельного внимания и тщательного изучения API.
    Текстовый ввод не удобен (т.к. пульт). Если у вас много элементов управления (текстовых полей, кнопок) или используется тач, то придётся все эти моменты перерабатывать.
    4. Аудитория. Может я не прав, но если сравнивать со всей массой пользователей андроид устройств её почти нет.
    Вживую телефон с андроидом я видел у многих. Телевизор с Android TV только один.
    5. Как операционная система Android для телевизоров удобней чем Smart TV. Это лично мое мнение, я могу сравнить, т.к. дома у меня телевизор LG с webOS, а на работе тестируем на Sony с Android-ом.

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

    Стоит ли следить за развитием платформы? Да, однозначно стоит. Бросить всё, уволиться с работы, продать машину и квартиру и начать делать ещё один клон “ангри бёрдс” под Android TV, в надежде что он вас озолотит — на мой взгляд не самая здравомыслящая идея.

    Делать прогнозы сложно, но вряд ли эта платформа даст такой же взрывной рост как и обычные мобильные приложения. Android TV — очень специфичный продукт.
    В целом, ситуация с ним чем-то напоминает бум умных часов. Было очень много маркетинговой шумихи, а в итоге никакого стремительно роста никто пока не наблюдает. Эти умные часы я тоже купил, вроде как на всякий случай, думал буду что-то писать под них, но пока они просто лежат у меня в ящике рабочего стола.

    PS: Хотел уточнить, чтобы не возникло искаженное понимание ситуации, т.к. я с одной стороны критикую платформу, а с другой стороны пишу что мы под нее активно разрабатываем. Да я считаю, что нет особого смысла в обычных приложениях под Android TV. У нас совсем другой случай, мы делаем специализированный продукт для B2B-сектора по заказу. Другими словами, пишем приложение специально предназначенное для работе на телевизоре.

  • FWD: Just Say mNo to Hungarian Notation!

    mNo

    Не могу пройти мимо призыва Джейка Вартона – “Скажи mNo венгерской нотации!“.

    Во-первых (и это мое личное мнение), использовать такую нотацию в исходниках на Java действительно нелепо.
    Во-вторых, когда-то давно я grep-ом проходил исходники андроида (кажется 16-ый API Level) и там нотация не соблюдалась. Более того она была некорректной (статические поля с m-префиксом).

    Вартон молодец, что наконец-то поднял эту тему.

  • Заметка об экспериментах со SnappyDB (NoSQL KeyValue DB под Android)

    Небольшая заметка про SnappyDB. Это NoSQL база данных под Android, которая базируется на LevelDB и алгоритме сжатия Snappy.

    LevelDB – это key-value база данных. Написана Google-ом для каких-то своих мега проектов.
    Snappy – метод сжатия данных, сбалансированный на скорость (т.е. приоритет быстрота, а не степень сжатия). Также написана Google-ом.

    Авторы SnappyDB на своем блоге приводят очень привлекательный график сравнивая скорость работы SnappyDB с SQLite.

    Все это конечно очень и очень заманчиво, но лично меня она заинтересовала не поэтому. Дело не в скорости и даже не в том, что какие-то части этой БД делал Сам Google, а в более-менее нормальном API.

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

    Конечно по-хорошему можно использовать API Preferenes или SQLite, но в некоторых случаях это не очень удобно.
    В частности у SQLite несколько громоздкий API, а в preferences нет удобных методов для сложных выборок по ключам.

    Упоминая более-менее нормальное API я имел в виду следующее. Вот что нужно сделать чтобы получить значение по ключу в Preferences (Android API).

      SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
      String username = sharedPref.getString("name", "");
    

    Вот что нужно сделать чтобы получить значение по ключу в Snappy DB.

      DB db = DBFactory.open(context); 
      String username =  db.get("name");
    

    Другими словами, API не такое “многословное” как стандартное Android API.

    При этом если есть несколько записей (“name:0″,”name:1″,”name:2” и т.д.), то их можно получить как-то так:

      db.findKeys("name:");
    

    Такое “из коробки” в Preferences сделать пока невозможно.

    Поэтому мне показалось, что Snappy DB это удобно. Можно использовать например в таких задачах как:
    – Сохранить справочников кодов и их описание
    – Запоминать время работы каких-то специальных команд или хранить данные по мониторингу
    – Идентификатор пользователя и его профиль (например в XML или JSON формате)

    В общем все было хорошо, пока не наткнулся на одну приставку под Android TV, на которой был установлен Android M. Так вот, на этом устройстве Snappy DB внезапно стала работать с ошибками. Поскольку БД нативная, а желания дебажить сишный код у меня в последнее время нет, пришлось отказаться от ее использования в текущих проектах.