Category: Java

Как определить ставить ли новую Windows или любую другую программу

Эволюция Корзины

Среди IT-специалистов распространено мистическое убеждение:

Считается, что каждая четная версия Windows выходит у Microsoft неудачной, а каждая нечетная, наоборот, хороша. Здесь стоит вспомнить критику Windows Millenuim, Windows Vista и Windows 8, которые были четными, а также успех Windows XP и Windows 7. Microsoft пропустила цифру «девять», сделав Windows 10 нечетной в этом списке. Впрочем, некоторые специалисты «девяткой» считают Windows 8.1, что переводит «десятку» в совсем другую категорию.

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

Вспоминаем: после XP люди ждут очень перспективной ОС с ещё более красивой графикой, гиберацией, повышенной безопасностью и возможностью не перезагружать комп после установки программ или драйверов. Появилась Vista. Люди негодуют — глюки, производительность никакая и т.п.
Разработчики чешут репу, многое отключают в системе чтобы не мешало, исправляют баги, что-то добавляют и выпускают семёрку.
Пользователи смотрят — производительность лучше чем у висты, радуются и забывают, что XP работала быстрее (момент немного спорный) и требовала намного меньше ресурсов для компьютера.
microsoft начинают воевать со старушкой XP, так как она остаётся неплохим конкурентом семёрке и активно используется в Китае вместе с IE 6

Сейчас повторяется то же самое: обещают поддержку ARM процессоров и телефонов, совместимость со старыми приложениями и возможность играть в игры для Xbox, революционный интерфейс и т.п.
Когда восьмёрку выпустят внезапно поймут что погнались за двумя зайцами, есть много багов, на телефонах тормозит и т.п. и т.д.
Разработчики почитают гневные реплики пользователей и к следующей (наверно, девятой) версии всё исправят. Судя по всему, ещё лет пять-десять как минимум семёрка будет оставаться одной из используемых ОС для компьютеров.

Впрочем, неудачная (если вдруг) Windows 8 не понравится некоторым пользователям, и они перейдут на linux или продукцию компании Apple, которая в последнее время имеет свободных денег больше чем microsoft. Не будем забывать и про Google, которые, в принципе, тоже могут что-то выпустить.
(с) http://forum.boolean.name/showthread.php?t=15523

ИЧСХ и всё прямо так и случилось!

Молоток как эволюция Виндовс

Поэтому есть релизы стабильные или успешные а есть тестовые или неуспешные. Понять успешность можно лишь по статистике как быстро на него переходили («адоптировали»). Я например за вистой и восьмой виндой даже не поработал. Иногда когда вижу у кого-то удивляюсь что это такое.

И это касается не только Виндовс но и других крупных продуктов с длинным релизным циклом, обычно у «коробочных» типа Microsoft Office. Но есть интересный пример версии Java:
1.0 Её даже не публиковали, так что скипнем
1.1 Первая опубликованная, ажиотажа не вызвала. Помещалась на дискету. Можно считать релиз неуспешным.
1.2 Появились коллекции, JIT, Swing, Java plugin и аплеты (а тогда это был крутяяяк) — количество кода библиотек выросло в три раза и уже на дискету не влезало. Успех!
1.3 Вообще ниочём, багфикс. Неуспех.
1.4 Регекспы, уродский логинг АПИ, но зато NIO, криптография, XML парсинг. Вроде ничего такого особого, но Успех. Началось активное использование для веба, сформировались библиотеки и фреймворки, появились первые JVM языки Groovy и Scala.
1.5 Синтаксический сахар (статик импорты, аннотации, for each, enum, varargs) но главное что появился внятная Java Memory Model и Concurrency. Внезапно успех, хотя почти на всех старых джава проектах этот на этот сахар ещё не переписали. Но грамотно заложенная и чётко специфицировання JMM сыграла
1.6 Последний релиз Sun’а ничем не интересный кроме внятных процессоров аннотаций. Всё замерло на этом релизе надолго и все только и любая магия так и делалась на аннотациях пока по ночам мы плакали в подушку мечтая о лябдах. Тем не менее релиз оказался довольно стабильным и всё работало много лет, так что всё таки успех.
1.7 Первый релиз Оракла. Ничего интересного, просто промежуточный перед восьмой джавой. Неуспех.
1.8 Ляяямбдыыыыы! И наконец-то внятный Date API. Все перешли на восьмую почти мгновенно. Успех!
1.9 Уже на выходе, по сути будет только улучшена модульность что конечно тоже сверхважно для древних энтерпрайзных монолитных проектов, но хипстеры уже всё говнокодят на микросервисах где всё проще переписать и не нужно сильно запариваться архитектурой. Так что особо ажиотажа особого не будет.

Итак, тут закономерность чётности версий прослеживается ну только при очень большом старании. Но если посмотреть не каждую вторую, а каждую третью версию то можно заметить что 1.2, 1.5 и 1.8 то эти релизы были особенно успешными. Причём даже не так, они наделали шума. В основном из-за того что в них появлялись новые «Вау» фичи. И это принципиальное дело кстати.
Представьте себя менеджером продукта. У вас есть некие срочные задачи — например секьюрити уязвимости, есть задачи по долгосрочному плану улучшения архитектуры проекта, но периодически вам нужно добавлять в продукт что-то такое что снова привлечёт внимание пользователей и даже те кто пользовался древней версией и довольны захотели бы купить новую версию. Например в случае с Джавой это новые возможности языка.
Т.е. есть разные задачи с разными характеристиками по важности и срокам и при грамотном менеджменте ты их учитываешь. Если их граммотно разложить и расписать на графике то можно найти множество таких закономерностей и предсказывать свойства следующей версии.
Поэтому можно смело предсказывать что например в десятую версию Джавы будут внесены какие-то вау фичи.

Но что самое интересное, если посмотреть на минорные версии джавы то там это правило чётности уже заметили давно и с пятой джавы даже приняли как схему версионирования: чётные релизы содержат новый функционал, нечётные багфикс и секьюрити апдейты. А позже ещё и усложнили настолько что даже пришлось на конференциях объяснять как теперь с этим разбираться

Но не всегда четно-нечётная схема усложняется, многие как раз наоборот стараются перейти к более частым и но последовательным и плавным rolling releases и continuous delivery:

Между сериями 1.0 и 2.6.x ядро Linux использовало нечётные номера для бета-версий, и чётные — для стабильных. Например, Linux 2.3 был серией в разработке, а Linux 2.4 — серией стабильных релизов, в которую перерос Linux 2.3. В номере релиза Linux kernel сначала писался номер второстепенной версии, а затем номер релиза в возрастающем порядке. Например Linux 2.4.0 → Linux 2.4.22. После релиза 2.6 в 2004 году Linux больше не использует эту систему, теперь цикл релиза намного короче. Сейчас они просто увеличивают третье число, используя при необходимости четвёртое.

Тоже самое кстати происходит и с броузером Chrome а позже и с Firefox’ом. Какая версия Хрома уже никто не знает (у меня например уже 16.3.0.6796). Для обеспечения надёжности они просто создали отдельную «вечную бету» которую могут себе поставить и протестировать все желающие и Feature toggling.

Непрерывная поставка
Таким образом цикл релиза и фидбека ускорился и подобные схема рано или поздно будет использована почти во всех программах.
И вы не отставайте 😉

Скринкаст: Пишем калькулятор на Java, JavaFX, Groovy, Spock, Kotlin

Несколько вечеров за которые я написал калькулятор на Джаве с тестами на Груви а потом переписал на Котлин. Интересно будет только для Stong Junior’ов

Это не туториал! В обычных туториалах всё хорошо и сразу получается, но как только начинающий программист сталкивается с ошибкой то не знает как дальше двигаться. Здесь все по настоящему — часть я знаю, часть изучаю сразу же. На ваших глазах появляются и фиксятся баги, гуглится и копипастится с СтекОверфлоу. Это может быть вам полезно для того чтобы ощутить эвристики которые помогают мне.
Скучно, занудно — но именно так примерно мы и программируем целыми днями. Если вы думаете связать свою жизнь с этим ремеслом, можете прочувствовать себя в моей шкуре 😉

Исходный код:
https://github.com/stokito/javafx-calculator

P.S. Да, я знаю звук отвратительный, винда неудачно обнволялась и с драйвером звука какая-то ерунда.

junit, test-ng, mockito, spock

Spock or not to Spock?

В последнем выпуске подкаста Разбор полётов ведущие спорили насколько оправданно применять тестовый фреймворк Spock для всех Java проектов.
Изначально разговор зашёл про спецификации и программные TCK которые проверяют что имплементация соответствует спецификации.
Тут пожалуй стоит сделать отступление, и отметить что обычно также делают referrence implementation и как минимум ещё одну реализацию, поскольку любой API нужно реализовывать всегда как минимум дважды чтобы увидеть не появляется ли повторяющийся вспомогательный код перед вызовом API метода который следовало бы тоже вынести в API.
Например, в Java SE для такой частой операции как чтение текстового файла в строку приходилось постоянно писать код преобразования InputStream в StringBuilder и у него уже брать toString(). И только в седьмой джаве появился метод Files.readAllBytes() тем не менее по прежнему требующий ещё дополнительной строчки преобразования массива байтов в строку с указанием кодировки.

Ещё один важный момент, что TCK являются для реализации внешними тестами, в то время как у неё могут быть свои собственные, тестирующие именно особенности реализации.
Я это всё расписал чтобы иметь более полную картину.

И тут у ведущих возник естественный вопрос: если у нас уже есть программный TCK, то, по сути, он уже и есть вообщем-то спецификацией, только записанной уже в более формальном языке программы.
Это совершенно правильная мысль. Единственное различие в них в том, что спецификация является текстом описывающим систему для людей, на естественном языке, в то время как от TCK требуется только дать однозначный ответ — соответствует реализация спецификации или нет. И очень вероятна ситуация когда по коду TCK мы вообще не сможем понять почему все работает именно так, поскольку детали опущены в коде TCK но расписаны в самой спецификации.
Вообще тут даже можно утверждать что refference implementation и тесты самой реализации тоже так же могут служить заменой или дополнением спецификации.
Это одна из причин почему программисты так любят open source — ты всегда можешь узнать как точно работает система на которую ты полагаешься.

Следовательно идея совместить спецификацию и тесты и сделать их ближе к коду родила BDD.
Обычно в BDD фреймворках пишут сценарий на, хоть и формализованном, но естественном языке а потом к нему пишут тестовый код.
Вот пример такого сценария на языке сценариев Gherkin для фреймворка Cuccumber:

Story: Returns go to stock

In order to keep track of stock
As a store owner
I want to add items back to stock when they're returned

Scenario 1: Refunded items should be returned to stock
Given a customer previously bought a black sweater from me
And I currently have three black sweaters left in stock
When he returns the sweater for a refund
Then I should have four black sweaters in stock

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

private Game game;
private StringRenderer renderer;

@Given("a $width by $height game")
public void theGameIsRunning(int width, int height) {
game = new Game(width, height);
renderer = new StringRenderer();
game.setObserver(renderer);
}

@When("I toggle the cell at ($column, $row)")
public void iToggleTheCellAt(int column, int row) {
game.toggleCellAt(column, row);
}

@Then("the grid should look like $grid")
public void theGridShouldLookLike(String grid) {
assertThat(renderer.asString(), equalTo(grid));
}

Но например для Groovy, динамического языка программирования поверх JVM многих ограничений Java нет. И поверх него был создан прекрасный фреймворк Spock. Вот пример теста\спецификации:

class HelloSpock extends spock.lang.Specification {
def "length of Spock's and his friends' names"() {
expect:
name.size() == length

where:
name | length
"Spock" | 5
"Kirk" | 4
"Scotty" | 6
}
}

Это data driven тест с двумя секциями expect и where. В секции expect мы пишем выражение которое проверяем, а в секции where мы объявили набор данных из трёх имён которые будут последовательно подставляться в это проверочное выражение expect.
Имя тестового метода объявлено в кавычках и может содержать пробелы и записано в человекочитаемом виде.

Он оказался настолько хорош, что один из ведущих, Барух, выдвинул тезис что именно Spock следует применять во всех проектах на Java для тестирования.
Из моего опыта я могу полностью подтвердить что это качественно новый уровень удобства тестирования недоступный другим фреймворкам и я полностью согласен с Барухом.

Но тут возникает несколько опасений.
1. Не все захотят изучать новый язык программирования Groovy для всего лишь тестов.
Я считаю что Groovy, в оличие от например Scala, это просто диалект Java. Почти любой код на Java является валидным кодом на Groovy. Можно прям взять файл и поменять расширение с .java на .groovy и он скомпилится. Поэтому по сути программистам можно даже не выучивать его для написания тестов.
Groovy очень простой и выразительный язык, имеющий знакомый всем синтаксис, довольно прямолинейный и обладающий отличной «вкуриваемостью».
Есть совсем небольшой гайд для тех кто переходит с Джавы и несколько статей на подобии Groovy за 15 минут. Как по мне, самое главное и что стоит выучить поскольку оно моментально сокращает код в разы (а то и в десятки раз!) — это литералы [] для коллекций и [:] для map, работа с коллекциями, null safe navigration и Groovy Truth. Выучив только эти вещи у вас будет Quick Win.

У меня был случай когда я устроился на работу Java девелопером где в описании вакансии было скромно сказано «будет плюсом знание Groovy & Grails». Я даже особо внимание не обратил, подумал что может где-то у них есть проект и может меня доучат и попросят там помочь. На собеседовании были стандартные вопросы по Джаве.
И в первый же рабочий день мне дали задание написать небольшой функционал на Groovy. Уже через час мой код был оттестирован, проревьювлен и закомичен.
Конечно, очень много времени потом ушло на разборки с всплывшими подводными камнями, всякие тонкости и выработки оптимально стиля кодирования. Также мне потребовалось изучить механизмы метапрограммирования для создания всевозможных DSL’ов.
Но, тестовый код — он всегда простой. Обычно тебе всё что нужно это вызвать метод и сверить с ожидаемым результатом. Поэтому если у вас продакшен код написан на стандартной Джаве а тесты на Груви то вы почти наверняка не столкнётесь с никакими проблемами и глубже его изучать не будет нужды.
Груви подключается как обычная dependency к проекту и имеет отличную поддержку в IntelliJ Idea. А для Спока в неё ещё есть плагин который правда непонятно зачем нужен.
По своему опыту могу сказать что мне было намного труднее вникнуть в стримы восьмой Джавы чем выучить Груви. Учитыая количество подводных камней даже JavaScript в разы будет сложнее Груви.
Так что если какой-то из разработчиков не сможет постигнуть премудрости Груви для написания тестов то он просто совсем некомпетентен.

2. Груви динамический язык, и хотя и есть возможность помечать классы как @CompileStatic но это не работает в Spock
Да, это так и потенциально есть вероятность что если, например, вы поменяли в продакшен коде имя метода а тестах забыли то в момент компиляции ошибка останется незамеченной.
Но, это совершенно не критично — вы сразу же увидите ошибку когда запустите тест и он завалится.
Кроме того, если пользоваться автоматическим рефакторингом в Idea то таких ошибок даже не возникнет.
Так что проблема совершенно надуманная.

3. Что такого даёт Спок что ради него стоит заморачиваться?

Спок тебя строго заставляет писать тесты в правильной структуре, например строго выдерживать секции given:, when:, then:. Я очень часто видел в тестах на JUnit когда эти логические секции то и дело смешиваются и программисты ленятся выносить их отдельный тест кейс.
Кроме того, все строки в секции then: автоматически являются assert и в совокупности с Groovy Truth это убирает кучу ненужного кода и читабельность невероятно возрастает.

И тут нужно внимательно остановится на асертах. В Споке появились так называемые Power Asserts — штука настолько потрясающая, что её позже перенесли в сам Groovy а позже и на другие языки программирования, но к сожалению кроме Джавы.
Вместо тысячи бестолковых assertEquals(), assertTrue() и всяких матчеров типа Hamcrest в Груви достаточно просто написать ключевое слово assert и вы получите подробный вывод что и где пошло не так:

assert [[1,2,3,3,3,3,4]].first().unique() == [1,2,3]

// Output:
//
// Assertion failed:
// assert [[1,2,3,3,3,3,4]].first().unique() == [1,2,3]
// | | |
// | | false
// | [1, 2, 3, 4]
// [1, 2, 3, 4]

На практике это просто Вау фича! Причём теперь фича самого Груви, так что даже обычный JUnit тест на Груви будет в тысячу раз приятнее.

Ну и лично моя самая любимая фича, это data driven tests. Спок даёт простой и удобный DSL для быстрого написания набора тестовых наборов. Я уже приводил пример выше, но у Николая Алименкова возникло сомнение насчёт их идеалогической правильности.
Когда один из наборов из where: не проходит то весь тест кейс помечается как failed, даже если остальные проходили.
В результате репортинг становится не очень полезным.

Если вы хотите видеть детальный отчёт какой именно тестовый набор провалился, вы можете пометить тест кейс аннотацией @Unroll.
Но Николай вполне справедливо заметил: хорошо, теперь я буду знать какой конкретно тестовый метод не прошёл, но я не буду знать почему, ведь комментария нету.
Следовательно для того чтобы оставить комментарий всё равно придётся дата дривен тест разбивать на несколько тест кейсов.

Когда я столкнулся с точно такой же проблемой я тоже расстроился и хотел было уже разбивать несколько тест кейсов когда смекнул как это красиво решить.
К аннотации @Unroll можно передать шаблон в который будут вставляться значения из data table. И ничто нам не мешает в дата тейбле сделать ещё одно поле с комментарием.
Например, допустим у нас есть валидатор пароля и тест на него может выглядеть так:

@Unroll
def 'checkPassword(#password) valid=#valid : #comment'() {
given:
PasswordValidator validator = new PasswordValidator()
expect:
validator.validate(password) == valid
where:
password | valid | comment
'pwd' | false | 'too short'
'very long password' | false | 'too long'
'password' | false | 'Not enought strength'
'h!Z7abcd' | true | 'valid password'
}

Чётко, кратко и по делу. Такой же тест на JUnit был бы больше в четыре раза и в десять раз менее читабельнее.
Легкость создания дата дривен тестов тебя прям подталкивает их писать — обычно нужно всего-то лишь добавить одну строчку.

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

Я например писал тесты для Grails Cookies плагина и просто посмотрите сколько кейсов в нём покрыто CookieResponseSpec.groovy.

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

Вообщем, подытожу немного перефразировав Баруха: Если вы пишете тесты не на Споке — вы занимаетесь хернёй.

Кстати сегодня выйдет пятая версия JUnit с поддержкой лямбд. Я хоть и донейтил даже на этот проект, но посмотрел на документацию и взгрустнул.
В обозримом будущем в мире Джавы пока ещё даже не предвидеться ничего лучше чем Спок.

Antipatern: Negatively named boolean method

There is an inspection Java | Data flow issues | Negatively named boolean variable

Reports negatively named variables, for example ‘disabled’, ‘hidden’, ‘isNotChanged’. It is usually more clear to invert the boolean value and remove the negation from the name.

But no any inspections for methods.
For example the Apache Commons Lang library’s StringUtils class has two methods: isBlank() and isNotBlank(). The second one has a negativation «Not» and works like !isBlank().
This is antipattern, because we can use automatic refactoring from intellij to invert condition that calling this method.
I mean this refactoring Invert Boolean and Invert If-Else Statement intention.
For examle we can invert a conditionwith isBlank():
invert-if-condition
And result of this refactoringwill be:
invert-if-condition-result

But if you invert a condition with isNotBlank() you willl get a double negativation smell:
invert-if-not-condition
Now it’s readed as «not not blank» (i.e. blank).

Another one solution is to use method names that hasn’t inversion, for example hasText()

 

Please, avoid this in you code
We will create an Inspection for IntelliJ Idea that will check this cases

[LinkSet] Compatibility

Theoretical part

 

Articles from Oracle

http://JEP 223: New Version-String Scheme

 

Six kinds of compatibility

Each Joda-Time relase has descriptions of incompatible changes categorized by six kinds:

Compatibility between 2.8 and 2.9
———————————
Build system — Yes
Binary compatible — Yes
Source compatible — Yes
Serialization compatible — Yes
Data compatible — Yes
— DateTimeZone data updated to version 2015g
Semantic compatible — Yes

When binary compatibilities are broken then Majour Version changed. For example v2.0 changelist

Explanation from  Stephen Colebourne, author of Joda-Time:

Build system

Not part of compatibility, just a fact about the build system in use

Example:  in v2.2

 - Ant build removed. Build only on Maven now.

Also, I think, it may be changing in artifact coordinates: groupId, artifactId or even changed repository. Maybe some changes in manifest, for example required dependencies. Or added OSGI manifest info. Maybe some classes was repackaged. Or old artifact was built with ANT and doesn’t contains a Maven’s pom.xml manifest like horrible Xerex library  and then was mavenized.

It also may be recompilation with optimizations or without debug information.

But I think that changing in packaging may be a separate kind of compatibility change.

Binary compatible and Source compatible

Whether the whole API is binary compatible or source compatible. See this document

Serialization compatible

Whether the serialization format is compatible with the previous version.

Data compatible

Whether the data, time-zone data in this case, has changed.

For example: some time zone was changed or even removed. If your database stores old timezone id and application trying to create a date object with this timezone id you’ll get an exception.

Semantic compatible

Whether there is some other semantic change. This is relevant when the classes and methods have not changed, but the meaning of a method has.

See section Serialization incompitables below

For example:  v2.0 has a lot of semantic changes

Previously, DateTimeZone.forID matched time zone names case-insensitively, now it is case-sensitive

I think it always hard to separate semantic changes when dealing with bugs. For example, JDK has bug when using null as key in Map

And that was a question is this a bug or feature.

Another one example, is Apache Commons Lang library. In version 3 the methods StringUtils.isAlpha(), isNumeric() and isAlphanumeric() now all return false when passed an empty String. Previously they returned true.

Semantic looks like correlated with behavior compatibility. When changed not signatures but they flow or contract.

Also it’s often situation when behavior was just undefined by contract. For example old class Vector doesn’t declare behavior about removing elements during iteration.  That’s why was created new class ArrayList and Vector was accepted as Superseded.
So, deprecation, as any changes in contract may be also some kind of semantic change.

 

Serialization incompitables

Compatible incopatibility

Also I think that it should be some kind of «reverse compatibility» that means a contract usage. For example, I saw an issue when subclass of HashMap doesn’t follow a contract. This change was compatible in all ways but all clients become incompatible. How to predict it, I don’t know.

Each of developer has experience when you can’t change something in your API because there is some clients that abuse it or implemented incorrectly. So it should have it’s own classification and developers should think about this kind of reverse incompatibility.

Complexity compatibility

I mean the complexity of program in wide sense: algorithm speed, it’s derivate and consumption of resources (memory, i/o, CPU, disk space), and even source code beautification and structural changes like refactoring. You know,  some algorithms may use more memory but use low processor.

For example, in some early versions of 64 bit JDK your application may fail with OutOfMemory error. This was change absolutely compatible in all categories mentioned before.

Another one sample is when new version of program is working more slowly than previous.

It may not change a contract and flow or anything else may it’s also may be changes that requires an attention.

 

Tool for checking API and BPI

Service provider interface (SPI)

https://stackoverflow.com/questions/2954372/difference-between-spi-and-api
https://en.wikipedia.org/wiki/Service_provider_interface

Also interesting

JAR Hell

[LinkSet] Dependency duplicates in Maven and Jar Hell with Java Classloader

Theoretical part:
Java Classloader

Maven: Introduction to the Dependency Mechanism

What is JAR hell? (Or is it classpath hell? Or dependency hell?)

Jar Hell made Easy — Demystifying the classpath with jHades
See JHades documentation, it very useful to find overlapping jars.

Another tool for dealing with Jar Hell is Weblogic Classloader Analysis Tool.

One of the most problematic dependency is Xeres Xerces Hell. It’s a good example how not to do library.

This presentation is a great resource about Jar Hell and the different type of classpath related exceptions:

But little bit boring.

Maven Dependency Wrangling

dealing with dependency chain issues in maven

Maven Enforcer plugin has extra rule
Ban Duplicate Classes
Also it should be very useful if you have legacy project that still runs under Java 6 or 7 you should avoid dependencies compiled with never Java 8.
You can use enforceBytecodeVersion

Please also make sure that you also specified requireJavaVersion (to compile), requireUpperBoundDeps, requireReleaseDeps, requirePluginVersions and other useful standard rules .

Also if you have a submodules in project it will be also useful ono-extra-enforcer-rules

So, your enforcer rules may looks like:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-enforcer-plugin</artifactId>
    <version>1.4.1</version>
    <executions>
        <execution>
            <id>enforce</id>
            <goals>
                <goal>enforce</goal>
            </goals>
            <configuration>
                <rules>
                    <bannedPlugins>
                        <!-- will only display a warning but does not fail the build. -->
                        <level>WARN</level>
                        <excludes>
                            <exclude>org.apache.maven.plugins:maven-verifier-plugin</exclude>
                        </excludes>
                        <message>Please consider using the maven-invoker-plugin (http://maven.apache.org/plugins/maven-invoker-plugin/)!</message>
                    </bannedPlugins>
                    <requireMavenVersion>
                        <version>3.0.5</version>
                    </requireMavenVersion>
                    <requireJavaVersion>
                        <version>1.8</version>
                    </requireJavaVersion>
                    <requireReleaseDeps>
                        <onlyWhenRelease>true</onlyWhenRelease>
                        <message>No Snapshots Allowed!</message>
                    </requireReleaseDeps>
                    <requireUpperBoundDeps>
                        <!-- 'uniqueVersions' (default:false) can be set to true if you want to compare the timestamped SNAPSHOTs  -->
                        <!-- <uniqueVersions>true</uniqueVersions> -->
                    </requireUpperBoundDeps>
                    <reactorModuleConvergence>
                        <message>The reactor is not valid</message>
                        <ignoreModuleDependencies>true</ignoreModuleDependencies>
                    </reactorModuleConvergence>
                    <requirePluginVersions>
                        <message>Best Practice is to always define plugin versions!</message>
                        <banLatest>true</banLatest>
                        <banRelease>true</banRelease>
                        <banSnapshots>true</banSnapshots>
                        <phases>clean,deploy,site</phases>
                        <additionalPlugins>
                            <additionalPlugin>org.apache.maven.plugins:maven-eclipse-plugin</additionalPlugin>
                            <additionalPlugin>org.apache.maven.plugins:maven-reactor-plugin</additionalPlugin>
                        </additionalPlugins>
                        <unCheckedPluginList>org.apache.maven.plugins:maven-enforcer-plugin,org.apache.maven.plugins:maven-idea-plugin</unCheckedPluginList>
                    </requirePluginVersions>
                    <enforceBytecodeVersion>
                        <maxJdkVersion>1.6</maxJdkVersion>
                        <excludes>
                            <exclude>org.mindrot:jbcrypt</exclude>
                        </excludes>
                    </enforceBytecodeVersion>
                    <banDuplicateClasses>
                        <ignoreClasses>
                            <!-- example of ignoring one specific class -->
                            <ignoreClass>com.xyz.i18n.Messages</ignoreClass>

                            <!-- example of ignoring with wildcards -->
                            <ignoreClass>org.apache.commons.logging.*</ignoreClass>
                        </ignoreClasses>
                        <findAllDuplicates>true</findAllDuplicates>
                    </banDuplicateClasses>
                    <banCircularDependencies/>
                    <ForbidOverridingManagedDependenciesRule>
                        <excludes>
                            <!-- guava in parent is too old, so allow to override it -->
                            <exclude>com.google.guava:guava</exclude>
                        </excludes>
                    </ForbidOverridingManagedDependenciesRule>
                    <ForbidOverridingManagedPluginsRule/>
                    <ForbidDependencyManagementInSubModulesRule/>
                    <ManageAllModulesRule/>
                </rules>
            </configuration>
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>extra-enforcer-rules</artifactId>
            <version>1.0-beta-3</version>
        </dependency>
        <dependency>
            <groupId>net.oneandone.maven</groupId>
            <artifactId>ono-extra-enforcer-rules</artifactId>
            <version>0.1.1</version>
        </dependency>
    </dependencies>
</plugin>

Two attempts to find duplicated classes with Maven
Remove duplicate classes the agile way: Maven Duplicate Finder Plugin

Finding Duplicate Class Definitions Using Maven

Both of this plugins are discussed here:
Figuring out duplicate class definitions using the Analyze goal

Also maven-shade-plugin does check for overlapping classes during packaging of uber-jar.

Resolving conflicts using the dependency:tree -Dverbose
It shows which dependencies are duplicated (omitted for duplicate), with are evicted with newer version (version managed from 1.6) but it doesn’t show which dependencies was excluded.

Another one good thing that worst to do is enable Failing the build on dependency analysis warnings. Note: it binded to verify phase that runed after package.

JDEPS Java Dependency Analysis Tool from JDK 8

Also some related articles

Versions compitable

For example changelog of Joda-Time v2.9

Compatibility with 2.8

Build system — Yes
Binary compatible — Yes
Source compatible — Yes
Serialization compatible — Yes
Data compatible — Yes
— DateTimeZone data updated to version 2015g
Semantic compatible — Yes

See another [Linkset] Compatibility

Jigsaw

It is possible a situation when some two libraries wanting to use the same dependency but of different versions. Unfortunately, in this cases we can’t manage this and should use -nodep versions.
Finally this problem will be resolved in JDK 9 Jigsaw: a jar can be declared as a module and it will run in it’s own isolated class loader, that reads class files from other similar module class loaders in an OSGI sort of way.
This will allow multiple versions of the same Jar to coexist in the same application if needed.

Working with deprecation

Upgrading of dependncies may require to remove some old codebase that depends on them.
This is also should be done in right way, so here some links that may helps:
* JEP 277
* Dr. Deprecator Prescriptions: important things that you should know about obsolete Java API

Speed up maven build

It’s also related topic. The main reason why I decided to add it here is because usually during speeding up build you will find a lot of problems with dependency graph.
It will helps you to make yoir project more modulized. Also for example paralell build may fails if your tests are in conflict (shares same resources, for example integration tests may use the same port).

Dependency analyzers

Also useful

* jApiCmp japicmp is a tool to compare two versions of a jar archive
* Java API Compliance Checker: A Perl script that uses javap to compare two jar archives. This approach cannot compare annotations and you need to have Perl installed.
* Clirr: A tool written in Java that compares two libraries for binary compatibility. Tracking of API changes is implemented only partially, tracking of annotations is not supported. Development has stopped around 2005.
* JDiff: A Javadoc doclet that generates an HTML report of all API changes. The source code for both versions has to be available, the differences are not distinguished between binary incompatible or not. Comparison of annotations is not supported.
* revapi: An API analysis and change tracking tool that was started about the same time as japicmp. It ships with a maven plugin and an Ant task, but the maven plugin currently (version 0.4.1) only reports changes on the command line.

Power Assert in Vim

Power Assert for Java and others

Peter Niederwieser, creator of Spock, the best testing framework ever, also contributed to Groovy programming language a very handy feature: Power Asserts.

int x = 1;
assert x == 2;

// Output:
//
// Assertion failed:
// assert x == 2
//        | |
//        1 false

As you can see, we can see each value of each statement, and this is very cool, especially for unit tests.
It makes all this JUnit bullshit like assertEquals(), assertTrue, assertNull, and all matchers like Hamcrest absolutely unneeded.
I really hate all matchers like FEST or Hamcest, because they aren’t natural and repeat code itself.

So now you can breathe freely without any repeating yourself.

You can see a talk where Peter Niederwieser present Spock specifications
https://vimeo.com/33947244

Also Peter created Power Asserts lib for Scala
https://github.com/pniederw/expecty

But,
Unfortunately, you can’t make a Power Asserts in Java, because it doesn’t have an AST preprocessor.
UPD Now we have the library https://github.com/jkschneider/java-power-assert but with limitations

If you, like me, have a big project with tests written in Java+JUnit, the only one way that I found, is convert them to Groovy (in 99% just change file extension from Java to Groovy) or you can compile your tests with groovyc — it’s Groovy Compiler but compiles Java too as well.
After compiling with groovyc asserts in Java behaves like in Groovy. Maybe this is the only one case when something written in Java works differently when compiled with Groovy.

For example here is simple test in Java

import org.junit.Test

class UserTest {
@Test
public void testName() {
String username = "olololo";
assert username.equals("trololo");
}

}

After compiling with groovyc it’s decompiled bytecode will looks like:

// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.codehaus.groovy.runtime.powerassert.AssertionRenderer;
import org.codehaus.groovy.runtime.powerassert.ValueRecorder;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
import org.junit.Test;

public class UserTest implements GroovyObject {
public UserTestGroovy() {
CallSite[] var1 = $getCallSiteArray();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}

@Test
public void testName() {
CallSite[] var1 = $getCallSiteArray();
String username = "olololo";
ValueRecorder var3 = new ValueRecorder();

try {
CallSite var10000 = var1[0];
var3.record(username, 8);
Object var6 = var10000.call(username, "trololo");
var3.record(var6, 17);
if(DefaultTypeTransformation.booleanUnbox(var6)) {
var3.clear();
} else {
ScriptBytecodeAdapter.assertFailed(AssertionRenderer.render("assert username.equals(\"trololo\")", var3), (Object)null);
}

} catch (Throwable var5) {
var3.clear();
throw var5;
}
}
}

As you can see, it uses ValueRecorder to store all evaluated expressions and it will render a source code line. And it may be executed as class file (may require groovy-all dependency in classpath):

Assertion failed:

assert username.equals("trololo")
|        |
olololo  false

at org.codehaus.groovy.runtime.InvokerHelper.assertFailed(InvokerHelper.java:402)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.assertFailed(ScriptBytecodeAdapter.java:650)
at com.github.stokito.UserTest.testName(UserTest.java:9)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)

Unfortunately currently IntelliJ can’t compile Java files with Groovyc. It always compiles *.java files with javac.
I created an issue Allow groovyc as compiler for Java classes, please vote for it.

Power Asserts in other languages

Kotlin

does not exist but authors know about Power Assertions and had intentions to do them.

.NET languages

https://github.com/PowerAssert/PowerAssert.Net

JavaScript

https://github.com/power-assert-js/power-assert
https://github.com/power-assert-js/espower-typescript for TypeScript
https://github.com/power-assert-js/espower-babel
https://github.com/power-assert-js/babel-plugin-espower
https://github.com/twada/battlefield-sourcemaps
https://github.com/deepblue-will/coffee-power-assert for Cofee Script
https://github.com/twada/fly-espower

Ruby

There is a gem https://github.com/k-tsj/power_assert and here is presentation from author:
https://speakerdeck.com/k_tsj/power-assert-in-ruby

https://github.com/joker1007/rspec-power_assert
https://rubygems.org/gems/power_assert/versions/0.2.4
https://github.com/hsbt/minitest-power_assert
https://github.com/yui-knk/pry-power_assert for Pry REPL

Others

https://github.com/keygx/PAssert XCTest for Swift
https://github.com/rosylilly/power_assert.cr for Crystal (static Ruby)
https://github.com/ma2gedev/power_assert_ex for Elixir
https://github.com/gifnksm/power-assert-rs for Rust
https://github.com/haya14busa/vital-power-assert for Vim script
https://github.com/ToQoz/gopwt for Go

TODO: write about power assets in https://en.wikipedia.org/wiki/Assertion_(software_development)

Always use interfaces for dependency injection

I working on a big legacy project that uses Spring IoC. And when it started I see in console warning messages like:

WARN  org.springframework.aop.framework.Cglib2AopProxy - Unable to proxy method [public final javax.sql.DataSource org.springframework.jdbc.core.support.JdbcDaoSupport.getDataSource()] because it is final: All calls to this method via a proxy will be routed directly to the proxy.

Here a sample code:

@Service
@Transactional
public class UserServiceImpl implements UserService {

}

@Controller
public class UserController {

    @Autowired
    UserServiceImpl  userService; // Here a problem: we declared field as concrete class UserServiceImpl  instead of interface UserService

}

As I found it happens when fields autowired as concrete class instead of injection by interface.

According to Spring AOP documentation:

Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. (JDK dynamic proxies are preferred whenever you have a choice).
If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. All of the interfaces implemented by the target type will be proxied. If the target object does not implement any interfaces then a CGLIB proxy will be created.

Thus since userService injected by concrete class UserServiceImpl instead of interface UserService, so Spriong IoC tries to create a proxy class via CGLIB bytecode magic that shows this error message.

Also this CGLIB magic proxying may be disabled by configuration:

&amp;amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;amp;gt;
&amp;amp;lt;beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"&amp;amp;gt;

    &amp;amp;lt;ehcache:annotation-driven cache-manager="genericCacheManager" proxy-target-class="false"/&amp;amp;gt;
    &amp;amp;lt;tx:annotation-driven proxy-target-class="false"/&amp;amp;gt;
    &amp;amp;lt;aop:aspectj-autoproxy proxy-target-class="false"/&amp;amp;gt;
&amp;amp;lt;/beans&amp;amp;gt;

In this case this exception will be thrown during runtime:


Caused by: java.lang.IllegalStateException: Cannot convert value of type [com.sun.proxy.$Proxy56 implementing com.eample.services.UserService,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [com.eample.services.UserServiceImpl] for property ‘userService’: no matching editors or conversion strategy found


To fix this we just need to use autowiring by interface and this is in common simple refactoring.

Please vote for this feature request to make IntellijIdea helpful in this New inspection: Use dependency injection by interface instead of concrete class

See also:
Spring: Why do we autowire the interface and not the implemented class?
Why always have single implementaion interfaces in service and dao layers?

Performance differences

[LinkSet] Avoiding NullPointerException in Java