Знайомство з командою курсів стека Java на Hexlet

Привіт, Хабраюзер. Ми давно хотіли стати трохи ближче до тебе. І сьогодні настільки тривало що зріє ідея розповісти тобі більш докладно про те: «хто і які курси Java стека на Хекслет робить» — втілилася! Як думаю — стало зрозуміло: ми розповімо Вам про курси над якими ми працювали останні пів року, включаючи останній курс який ось-ось почали записувати: Java Web-сервісів.



Читати далі →

Виклик методів через reflection

Всі програмісти Java явно або неявно користуються reflection для виклику методів. Навіть якщо ви не робили цього самі, це за вас напевно роблять бібліотеки або фреймворки, які ви використовуєте. Давайте подивимося, як цей виклик влаштований всередині і наскільки це швидко. Будемо дивитися в OpenJDK 8 з останніми оновленнями.
Читати далі →

Краще в райнтайме, ніж ніколи: розширюємо API JIRA «на льоту»

Що робити, якщо наявного у додатку API для вирішення задачі недостатньо, а можливості оперативно провести зміни в код немає?



Останньою надією в цій ситуації може бути застосування засобів пакету java.lang.instrument. Всім, кому цікаво, що і як в Java можна зробити з кодом вже запущеної VM, ласкаво просимо під кат.

Читати далі →

Модифікація програми і що краще міняти: виконуваний код або AST програми?

Принципи в замітці загальні для майже будь-якої мови програмування та системи виконання, але акцент буде на jvm. Розглянемо два основних підходи щодо модифікації програми:

  • маніпуляції з виконуваним кодом програми після компіляції або під час завантаження коду;
  • зміна вихідного коду перед компіляцією.


Читати далі →

JCoro — асинхронність на сопрограммах в Java

До досліджень у цій сфері мене надихнула стаття Асинхронність: назад у майбутнє. У ній автор описує ідею про те, як, використовуючи співпрограми, можна спростити асинхронний код так, щоб виглядав він так само, як звичайний синхронний, але зберігав плюшки, які нам дає застосування асинхронних операцій. Коротенько, суть підходу така: якщо у нас є механізм, що дозволяє зберігати і відновлювати контекст виконання (підтримка сопрограмм), то код на ланцюжках callback'ів

startReadSocket((data) -> {
startWriteFile(data, (result) -> {
if (result == ok) ...
});
});

ми можемо переписати так:

data = readSocket();
result = writeFile(data);
if (result == ok) ...

Тут readSocket() і writeFile() — співпрограми, в яких асинхронні операції викликаються наступним чином:

byte[] readSocket() {
byte[] result = null;
startReadSocket((data) -> {
result = data;
resume();
});
yield();
return result;
}

Методи yield() і resume() зберігають і відновлюють контекст виконання, з усіма фреймами та локальними змінними. Відбувається наступне: при виклику readSocket() ми плануємо асинхронну операцію викликом startReadSocket() і виконуємо yield(). Yield() зберігає контекст виконання і потік завершується (повертається в пул). Коли асинхронна операція буде виконана, ми викличемо resume() перед виходом з callback'a, і тим самим відновимо виконання коду. Управління знову отримає основна функція, яка викличе writeFile(). writeFile() влаштований аналогічно, і все повториться.

Зробивши один раз таке перетворення для всіх використовуваних асинхронних операцій і помістивши отримані функції в бібліотеку, ми отримуємо інструмент, що дозволяє нам писати асинхронний код так, як ніби це звичайний синхронний код. Ми отримуємо можливість поєднувати плюси синхронного коду (читабельність, зручна обробка помилок) й асинхронного (продуктивність). Плата за це зручність — необхідність зберігати і відновлювати контекст виконання. У статті автор описує реалізацію на С++, мені ж захотілося отримати щось таке в Java. Про це і піде мова.

Читати далі →

Java байткод «Hello world»

На хабре вже є стаття про java байткод. Я вирішив її трохи доповнити і в міру сил розвинути тему. Мені здається досить логічним розібрати найпростіше додаток на Java. А що може бути простіше «Hello world»?
Для свого експерименту я створив директорію src, куди в папку hello поклав файл App.java:

package hello;

public class App {

public static void main(String[] args) {
System.out.println("Hello world!");
}

}



Читати далі →

Компіляція і декомпіляція try-with-resources

Компіляція і декомпіляція try-with-resources, або розповідь про те, як я фиксил баг і що з цього вийшло.

Введення
PITestЯкийсь час назад backlog робочого проекту майже спорожнів, і вгору спливли різного роду дослідницькі завдання. Одна з них звучала досить інтригуюче: прикрутити до проекту мутаційного тестування використовуючи PITest. На Хабре вже є вельми докладний огляд бібліотеки (з прикладами і картинками). Переповідати цю статтю своїми словами я не буду, але все ж рекомендую з нею попередньо ознайомитися.

Зізнаюся, що ідеєю мутаційного тестування я загорівся. Майже без додаткових зусиль отримати інструмент пошуку потенційно небезпечних місць коду — воно того варте! Я негайно взявся за справу. На той момент бібліотека була відносно молодий, як наслідок — дуже сирої: тут потрібно трохи пошаманити з конфігурацією maven'а там — пропатчити плагін для Sonar'а. Однак через деякий час я все ж зміг перевірити проект цілком. Результат: сотні вижили мутацій! Еволюція в масштабі на нашому build-сервері.

Засукавши рукави я поринув у роботу. В одних тестах не вистачає верификаций заглушок, в інших замість логіки взагалі незрозуміло що тестується. Правимо, покращуємо, переписуємо. Загалом, процес пішов, але число тих, що вижили мутацій зменшувалося не так стрімко, як хотілося. Причина була проста: PIT давав величезна кількість помилкових спрацьовувань на блоці try-with-resources. Недовгі пошуки показали, що відомий баг, але досі не виправлено. Що ж, код бібліотеки відкрито. Від чого б не скопіювати його і подивитися, в чому ж справа?
Читати далі →

Пул констант

  Багато хто знає, що в кожному. Class-файлі є чудова структура даних, яка називається пулом констант. Але далеко не кожен Java-розробник, дивлячись на ісходник, зможе навіть приблизно оцінити, скільки констант буде створено в пулі.
 
Візьмемо, наприклад, такий код:
 
 
System.out.println("Hello World!");

Він транслюється в три інструкції байткода: getstatic (для завантаження статичного поля System.out), ldc (для завантаження константної рядки «Hello World!") І invokevirtual (для виконання віртуальної функції println). Спробуйте прикинути, скільки констант потрібно для того, щоб цей код працював.
 
Читати далі →