Русский
Русский
English
Статистика
Реклама

Java 8

Сбор данных и отправка в Apache Kafka

15.11.2020 20:17:49 | Автор: admin

Введение


Для анализа потоковых данных необходимы источники этих данных. Так же важна сама информация, которая предоставляется источниками. А источники с текстовой информацией, к примеру, еще и редки.
Из интересных источников можно выделить следующие: twitter, vk. Но эти источники подходят не под все задачи.
Есть источники с нужными данными, но эти источники не потоковые. Здесь можно привести следующее ссылки: public-apis.
При решении задач, связанных с потоковыми данными, можно воспользоваться старым способом.
Скачать данные и отправить в поток.
Для примера можно воспользоваться следующим источником: imdb.
Следует отметить, что imdb предоставляет данные самостоятельно. См. IMDb Datasets. Но можно принять, что данные собранные напрямую содержат более актуальную информацию.


Язык: Java 1.8.
Библиотеки: kafka 2.6.0, jsoup 1.13.1.


Сбор данных


Сбор данных представляет из себя сервис, который по входным данным загружает html-страницы, ищет нужную информацию и преобразует в набор объектов.
Итак источник данных: imdb. Информация будет собираться о фильмах и будет использован следующий запрос: https://www.imdb.com/search/title/?release_date=%s,%s&countries=%s
Где 1, 2 параметр это даты. 3 параметр страны.
Для лучшего понимания источника данных можно обратится к следующему ресурсу: imdb-extensive-dataset.


Интерфейс для сервиса:


public interface MovieDirectScrapingService {    Collection<Movie> scrap();}

Класс Movie это класс, которые содержит информацию об одном фильме (или о шоу и т.п.).


class Movie {    public final String titleId;    public final String titleUrl;    public final String title;    public final String description;    public final Double rating;    public final String genres;    public final String runtime;    public final String baseUrl;    public final String baseNameUrl;    public final String baseTitleUrl;    public final String participantIds;    public final String participantNames;    public final String directorIds;    public final String directorNames;

Анализ данных на одной странице.
Информация собирается следующим образом. Данные закачиваются с помощью jsoup. Далее ищутся нужные html-элементы и трансформируются в экземпляры для фильмов.


String scrap(String url, List<Movie> items) {    Document doc = null;    try {        doc = Jsoup.connect(url).header("Accept-Language", language).get();    } catch (IOException e) {        e.printStackTrace();    }    if (doc != null) {        collectItems(doc, items);        return nextUrl(doc);    }    return "";}

Поиск ссылки на следующею страницу.


String nextUrl(Document doc) {    Elements nextPageElements = doc.select(".next-page");    if (nextPageElements.size() > 0) {        Element hrefElement = nextPageElements.get(0);        return baseUrl + hrefElement.attributes().get("href");    }    return "";}

Тогда основной метод будет таким. Формируется начальная строка поиска. Закачиваются данные по одной странице. Если есть следующая страница, то идет переход к ней. По окончании передаются накопленные данные.


@Overridepublic Collection<Movie> scrap() {    String url = String.format(            baseUrl + "/search/title/?release_date=%s,%s&countries=%s",            startDate, endDate, countries    );    List<Movie> items = new ArrayList<>();    String nextUrl = url;    while (true) {        nextUrl = scrap(nextUrl, items);        if ("".equals(nextUrl)) {            break;        }        try {            Thread.sleep(50);        } catch (InterruptedException e) {        }    }    return items;}

Подробности по остальным методам можно найти в ссылках на ресурсы.


Отправка данных в топик


Формируется следующий сервис: MovieProducer. Здесь будет один единственный публичный метод: run.


Создается продюсер для кафки. Загружаются данные из источника. Трансформируются и отправляются в топик.


public void run() {    try (SimpleStringStringProducer producer = new SimpleStringStringProducer(            bootstrapServers, clientId, topic)) {        Collection<Data.Movie> movies = movieDirectScrapingService.scrap();        List<SimpleStringStringProducer.KeyValueStringString> kvList = new ArrayList<>();        for (Data.Movie move : movies) {            Map<String, String> map = new HashMap<>();            map.put("title_id", move.titleId);            map.put("title_url", move.titleUrl);                        String value = JSONObject.toJSONString(map);            String key = UUID.randomUUID().toString();            kvList.add(new SimpleStringStringProducer.KeyValueStringString(key, value));        }        producer.produce(kvList);    }}

Теперь все вместе


Формируются нужные параметры для поиска. Загружаются данные и отправляются в топик.
Для этого понадобится еще один класс: MovieDirectScrapingExecutor. С одним публичным методом: run.


В цикле создаются данные для поиска из текущей даты. Происходит загрузка и отправка данных в топик.


public void run() {    int countriesCounter = 0;    List<String> countriesSource = Arrays.asList("us");    while (true) {        try {            LocalDate localDate = LocalDate.now();            int year = localDate.getYear();            int month = localDate.getMonthValue();            int day = localDate.getDayOfMonth();            String monthString = month < 9 ? "0" + month : Integer.toString(month);            String dayString = day < 9 ? "0" + day : Integer.toString(day);            String startDate = year + "-" + monthString + "-" + dayString;            String endDate = startDate;            String language = "en";            String countries = countriesSource.get(countriesCounter);            execute(language, startDate, endDate, countries);            Thread.sleep(1000);            countriesCounter += 1;            if (countriesCounter >= countriesSource.size()) {                countriesCounter = 0;            }        } catch (InterruptedException e) {        }    }}

Для запуска потребуется экземпляр класса MovieDirectScrapingExecutor, который можно запустить с нужными параметрами, к примеру, из метода main.


Пример отправляемых данных для одного фильма.


{  "base_name_url": "https:\/\/www.imdb.com\/name",  "participant_ids": "nm7947173~nm2373827~nm0005288~nm0942193~",  "title_id": "tt13121702",  "rating": "0.0",  "base_url": "https:\/\/www.imdb.com",  "description": "It's Christmas time and Jackie (Carly Hughes), an up-and-coming journalist, finds that her life is at a crossroads until she finds an unexpected opportunity - to run a small-town newspaper ... See full summary ",  "runtime": "",  "title": "The Christmas Edition",  "director_ids": "nm0838289~",  "title_url": "\/title\/tt13121702\/?ref_=adv_li_tt",  "director_names": "Peter Sullivan~",  "genres": "Drama, Romance",  "base_title_url": "https:\/\/www.imdb.com\/title",  "participant_names": "Carly Hughes~Rob Mayes~Marie Osmond~Aloma Wright~"}

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


Тесты


Для тестирования основной логики, которая связана с отправкой данных, можно воспользоваться юнит-тестами. В тестах предварительно создается kafka-сервер.
См. Apache Kafka и тестирование с Kafka Server.


Сам тест: MovieProducerTest.


public class MovieProducerTest {    @Test    void simple() throws InterruptedException {        String brokerHost = "127.0.0.1";        int brokerPort = 29092;        String zooKeeperHost = "127.0.0.1";        int zooKeeperPort = 22183;        String bootstrapServers = brokerHost + ":" + brokerPort;        String topic = "q-data";        String clientId = "simple";        try (KafkaServerService kafkaServerService = new KafkaServerService(                brokerHost, brokerPort, zooKeeperHost, zooKeeperPort        )        ) {            kafkaServerService.start();            kafkaServerService.createTopic(topic);            MovieDirectScrapingService movieDirectScrapingServiceImpl = () -> Collections.singleton(                    new Data.Movie()            );            MovieProducer movieProducer =                    new MovieProducer(bootstrapServers, clientId, topic, movieDirectScrapingServiceImpl);            movieProducer.run();            kafkaServerService.poll(topic, "simple", 1, 5, (records) -> {                assertTrue(records.count() > 0);                ConsumerRecord<String, String> record = records.iterator().next();                JSONParser jsonParser = new JSONParser();                JSONObject jsonObject = null;                try {                    jsonObject = (JSONObject) jsonParser.parse(record.value());                } catch (ParseException e) {                    e.printStackTrace();                }                assertNotNull(jsonObject);                    });            Thread.sleep(5000);        }    }}

Заключение


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


Ссылки и ресурсы


Исходный код.

Подробнее..

Автоматизация публикации приложения в Google Play при помощи Jenkins

26.01.2021 20:09:19 | Автор: admin

Для этого нам понадобится

  1. Действующий account Google Play Developer

  2. Сервер Linux с предустановленным Docker, в моём случае это Ubuntu 16.04

  3. Установленный Android SDK

  4. Jenkins - в данном случае развернём его при помощи Docker

  5. Gitea - Удобная служба для собственного Git-репозитория (это не обязательно можно использовать и GItHub) её мы подымем также на базе Docker контейнера

Настройка Google Play Account

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

  1. Авторизуйтесь в Google Cloud Platform, если еще не создан проект то создаём его

  2. В разделе IAM и администрирование - Сервисные аккаунты жмём Создать сервисный аккаунт

  3. После заполнения соответствующих полей он будет создан и появится в списке, жмём на три точки с права и выбираем создать ключ, выбираем JSON, сохраните его он нам понадобится для настройки Jenkins

  4. Авторизуйтесь в Gooogle Play Developer Console

  5. В разделе Пользователи и разрешения нажимаем пригласить пользователя сервисного аккаунта по сгенерированному email , выставляем ему роль Релиз менеджер (при необходимости можете настроить права этого пользователя более детально)

  6. Я предполагаю что Сертификат для ключа подписи приложения, и Сертификат ключа загрузки у вас уже настроен и это не требует пояснений.

Установка Android SDK

# Install latest JDKsudo apt install default-jdksudo apt install android-sdk

Добавьте Android SDK в свой PATH, откройте~/.bashrcв редакторе и скопируйте следующие строки

# Export the Android SDK path export ANDROID_HOME=$HOME/android-sdkexport PATH=$PATH:$ANDROID_HOME/tools/binexport PATH=$PATH:$ANDROID_HOME/platform-tools# Fixes sdkmanager error with java versions higher than java 8export JAVA_OPTS='-XX:+IgnoreUnrecognizedVMOptions --add-modules java.se.ee'

Для проверки запустите

source ~/.bashrc

Для того что бы просмотреть все доступные инструменты сборки выполните команду

sdkmanager --list

По аналогии с выполните установку интересующих вас версий инструментов для нужных платформ

sdkmanager "platform-tools" "platforms;android-28"

На этом установка Android SDK может считаться завершённой, и в дальнейшем нам понадобится лишь путь к установленным инструментам, в моём случае это выглядит как то так

Установка Gitea

Этот шаг является опциональным и совсем не обязательным если вы предпочитаете использовать git репозитарии такие как GitHub и им подобные и его можно пропустить, это малой степени повлияет на конечный результат. (На базе gitea в дальнейшем будет обсуждаться тема создания Telegram Bot`a для оповещения о публикациях)

Подробно по установке и настройке написано на оф сайте https://docs.gitea.io/en-us/install-with-docker/

По факту установка сводится к выполнению 2х действий

1) Перейдите на официальный репозитарий Gitea , на Docker HUB и скопируйте то что там написано в предварительно созданный файл

version: '2'services:  web:    image: gitea/gitea:1.12.4    volumes:      - ./data:/data    ports:      - "3000:3000"      - "22:22"    depends_on:      - db    restart: always  db:    image: mariadb:10    restart: always    environment:      - MYSQL_ROOT_PASSWORD=changeme      - MYSQL_DATABASE=gitea      - MYSQL_USER=gitea      - MYSQL_PASSWORD=changeme    volumes:      - ./db/:/var/lib/mysql

2) Сохраните и запустите команду docker-compose you_filename

3) Gitea установлена и доступна по URL http://you_IP:3000/

4) Создайте пользователя, репозитарий, сделайте PUSH исходного кода вашего проекта, и в моём случае для упрощения процесса публикации приложения все необходимые ключи для его подписи и деплоя я храню в месте с исходным кодом в системе контроля версий (да знаю это не совсем верно и вы вольны делать так как считаете нужным, к примеру создать отдельный volume для ключей и пробрасывать их jenkins для дальнейшего их использования gradle при подписании приложения, описание этого лишь раздует статью и потому не будет рассматриваться)

Предварительная настройка проекта

Для подписания нашего apk файла в автоматическом режиме на стороне сервера, нам нужен файл нашего keystore и правильно настроенный скрипт gradle , для этого добавим в него несколько секций

// Load keystoredef keystorePropertiesFile = rootProject.file("keystore.properties")def keystoreProperties = new Properties()keystoreProperties.load(new FileInputStream(keystorePropertiesFile))// GenerateNameVersiondef getVersionNameTimestamp() {    return new Date().format('yy.MM.ddHHmm')}// GenerateVersionCodedef getVersionCodeTimestamp() {    def date = new Date()    def formattedDate = date.format('yyMMddHHmm')    def code = formattedDate.toInteger()    println sprintf("VersionCode: %d", code)    return code}......android {    signingConfigs {        config {            keyAlias keystoreProperties['keyAlias']            keyPassword keystoreProperties['keyPassword']            storeFile file(keystoreProperties['storeFile'])            storePassword keystoreProperties['storePassword']        }    }......    defaultConfig {        versionCode getVersionCodeTimestamp()        versionName "${getVersionNameTimestamp()}"

Содержимое файла keystore.properties

storePassword=you_password_keystorekeyPassword=you_password_keykeyAlias=you_key_namestoreFile=path_to_keystore_file
  • Я храню как файл с keystore.properties так и сам keystore в системе контроля версий, что не совсем правильно и не безопасно при публикации исходного кода в открытом виде к примеру на GitHub, по этому я не рекомендую использовать данный подход если у вас OpenSource проект , храните и подтягивайте их из отдельной папки.

  • Для публикации в Google Play требуется уникальная версия сборки, и я генерирую её скриптом Gradle на основании текущей даты и времени, и как вы понимаете это исключает использование каких либо вразумительных номеров версий и если вам это важно , то вам придётся придумать некий другой механизм автоматизации версионирования, я же остановился на этом варианте - потому как хотел что бы deploy происходил по нажатию одной лишь кнопки (PUSH)

Установка Jenkins

Его мы развернём используя Docker , я применяю уже настроенный образ с нужными мне инструментами из своего репозитария, вы же вольны использовать официальный, принципиально ни чего не измениться

docker run -it -d -v jenkins_home:/var/jenkins_home -v /usr/lib/android-sdk:/usr/lib/android-sdk -p 8080:8080 -p 50000:50000 --env JENKINS_OPTS="--prefix=/jenkins" --restart always leganas/ls_repository:jenkins

пробрасываем в качестве volumes в в наш контейнер папку с установленным Android SDK

Теперь он запущен и доступен по адресу http://you_IP:8080/jenkins/

После не хитрых манипуляций по заданию пароля Jenkins готов к работе, и теперь нам предстоит его настроить

1) Для начала нам нужно настроить окружение , заходим System Configuration - Глобальные настройки , и добавляем Environment variables (возможно ваши пути будут отличатся)

2) В разделе Настройки - Конфигурация глобальных инструментов проверяем настройки Git и Gradle (по факту обычно там всё уже настроено)

3) Заходим Настройки - Управление пользователями , выбираем пользователя и его настройки, ищем строку API Token , создаём новый и сохраняем его , он нам понадобится.

4) В разделе управления плагинами проверяем и если нужно устанавливаем плагины Git, Git client, Google OAuth Credentials plugin, Google Play Android Publisher

5) Заходим Настройки - Manage Credentials Configure credentials -Store - Jenkins - Global credentials (unrestricted)- и создаём там 2 ключа доступа |

  • для доступа к Git репозитарию

    в моём случае т.к. я использую Gitea я создаю обычную пару login/password , для GitHub есть специальный плагин и инструмент для авторизации

  • для публикации приложения в Google Play Market

    создаём ключ используя JSON файл который создали в первом разделе данной инструкции

6) Теперь создайте проект вашего приложения в Jenkins и настройте его

  • Управление исходным кодом - установите Git , укажите Repository URL и Credentials (которые создали на прошлом этапе)

  • Триггеры сборки - выставите Trigger builds remotely (e.g., from scripts), в поле ввода введите любой случайный текст (он нам понадобится для удалённой активизации процесса сборки) при помощи GIt huck

  • Добавьте шаг сборки Invoke Gradle script , и После сборочные операции - публикацию

Обратите внимание на поле Release traack , оно указывает то как будет опубликовано ваше приложение , в моём случае это "Внутреннее тестирование"

7) Запустите проект в ручную использую Web интерфейс Jenkins и посмотрите на результат в истории сборок - Вывод консоли Если вы всё сделали верно то ваше приложение должно быть подтянуто из Git репозитария до актуальной версии ветки /master , собрано , подписано и опубликовано на Google Play.

Автоматизация запуска сборки

Если мы всё сделали правильно и проект удачно был опубликован на Google Play , можно перейти к настройке его автоматического deploy по Git событию PUSH в /master ветку

1) Если вы используете Gitea как я то зайдите в репозитарий вашего проекта - Настройки - Git хуки, и в post-receive нажмите редактировать, и добавьте нечто этого рода

#!/bin/bashwhile read oldrev newrev refnamedo    branch=$(git rev-parse --symbolic --abbrev-ref $refname)    if [ "master" = "$branch" ]; then       curl --user you_user_name:you_user_token http://you_url_jenkins/job/you_project/build?token=you_tocken_build    fidone
  • you_user_name - Имя пользователя от лица которого jenkins будет производить сборку

  • you_user_token - Токен который был сгенерирован в настройках пользователя

  • you_url_jenkins и вообще весь путь до хука можно посмотреть на странице настройки проекта Jenkins и будет выглядеть он примерно так :

После проведения такого рода манипуляций сборка проекта Jenkins будет начинаться автоматически после PUSH события в master вашего репозитария.

PS. Если же вы используете для хранения вашего кода другую Git систему то этот же код вы можете поместить руками в .git\hooks\post-update

Заключение

В случае положительного фидбэка от статьи, в следующей части я расскажу как написать Telegram бота для оповещения ваших тестировщиков о наличии обновления приложения на Google Play.

Надеюсь эта информация была полезна и как то облегчит вам жизнь по настройки автоматической публикации.

Подробнее..

Из песочницы Lambda-выражения в Java

28.07.2020 02:23:38 | Автор: admin
Привет, Хабр! Представляю вашему вниманию перевод статьи Java Lambda Expressions автора www.programiz.com.

Введение


В этой статье, с помощью примеров, мы изучим lambda-выражения в Java, их использование с функциональными интерфейсами, параметризированными функциональными интерфейсами и Stream API.

Лямбда выражения были добавлены в Java 8. Их основная цель повысить читабельность и уменьшить количество кода.

Но, прежде чем перейти к лямбдам, нам необходимо понимать функциональные интерфейсы.

Что же такое функциональный интерфейс?


Если интерфейс в Java содержит один и только один абстрактный метод, то он называется функциональным. Этот единственный метод определяет назначение интерфейса.

Например, интерфейс Runnable из пакета java.lang является функциональным, потому, что он содержит только один метод run().

Пример 1: объявление функционального интерфейса в java

import java.lang.FunctionalInterface;@FunctionalInterfacepublic interface MyInterface{    // один абстрактный метод    double getValue();}

В приведенном выше примере, интерфейс MyInterface имеет только один абстрактный метод getValue(). Значит, этот интерфейс функциональный.

Здесь мы использовали аннотацию FunctionalInterface, которая помогает понять компилятору, что интерфейс функциональный. Следовательно, не позволяет иметь более одного абстрактного метода. Тем не менее, мы можем её опустить.

В Java 7, функциональные интерфейсы рассматривались как Single Abstract Methods (SAM). SAM обычно реализовывались с помощью анонимных классов.

Пример 2: реализация SAM с помощью анонимного класса в java

public class FunctionInterfaceTest {    public static void main(String[] args) {        // анонимный класс        new Thread(new Runnable() {            @Override            public void run() {                System.out.println("Я только что реализовал функциональный интерфейс Runnable.")            }        }).start();    }}

Результат выполнения:

Я только что реализовал функциональный интерфейс Runnable.

В этом примере, мы принимаем анонимный класс для вызова метода. Это помогало писать программы с меньшим количеством строк кода в Java 7. Однако, синтаксис оставался достаточно сложным и громоздким.

Java 8 расширила возможности SAM, сделав шаг вперед. Как мы знаем, функциональный интерфейс содержит только один метод, следовательно, нам не нужно указывать название метода при передаче его в качестве аргумента. Именно это и позволяет нам lambda-выражения.

Введение в лямбда-выражения


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

Как записать лямбда-выражение в Java?


В Java, лямбда-выражения имеют следующий синтаксис:

(parameter list) -> lambda body

Здесь мы использовали новый оператор (->) лямбда-оператор. Возможно, синтаксис кажется немного сложным. Давайте разберем пару примеров.

Предположим, у нас есть такой метод:

double getPiValue() {    return 3.1415;}

Мы можем записать его, используя лямбда, как:

() -> 3.1415

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

Типы лямбда-выражений


В Java, тело лямбды может быть двух типов.

1. Однострочные

() -> System.out.println("Lambdas are great");

2. Блочные (многострочные)

() -> {    double pi = 3.1415;    return pi;};

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

Примечание: многострочные лямбда-выражения, всегда должны иметь оператор return, в отличии от однострочных.

Пример 3: лямбда-выражение

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

Как говорилось ранее, лямбда-выражение не выполняется само собой. Скорее, оно формирует реализацию абстрактного метода, объявленного в функциональном интерфейсе.

И так, для начала, нам необходимо описать функциональный интерфейс.

import java.lang.FunctionalInterface;// функциональный интерфейс@FunctionalInterfaceinterface MyInterface{    // абстрактный метод    double getPiValue();}public class Main {    public static void main( String[] args ) {    // объявление ссылки на MyInterface    MyInterface ref;        // лямбда-выражение    ref = () -> 3.1415;        System.out.println("Value of Pi = " + ref.getPiValue());    } }

Результат выполнения:

Value of Pi = 3.1415

В примере выше:

  • Мы создали функциональный интерфейс MyInterface, который содержит один абстрактный метод getPiValue().
  • Внутри класса Main, мы объявили ссылку на MyInterface. Обратите внимание, что мы можем объявить ссылку на интерфейс, но не можем создать его объект.

    // приведет к ошибкеMyInterface ref = new myInterface();// это верноMyInterface ref;
    

  • Затем мы присвоили ссылке лямда-выражение

    ref = () -> 3.1415;
    
  • В заключении, мы вызвали метод getPiValue(), используя ссылку на интерфейс.

    System.out.println("Value of Pi = " + ref.getPiValue());
    

Лямбда-выражения с параметрами


До этого момента, мы создавали лямбда-выражения без каких-либо параметров. Однако, как и методы, лямбды могут иметь параметры.

(n) -> (n % 2) == 0

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

Пример 4: использование лямбда-выражения с параметрами

@FunctionalInterfaceinterface MyInterface {    // абстрактный метод    String reverse(String n);}public class Main {    public static void main( String[] args ) {        // объявление ссылки на MyInterface        // присвоение лямбда-выражения ссылке        MyInterface ref = (str) -> {            String result = "";            for (int i = str.length()-1; i >= 0 ; i--)            result += str.charAt(i);            return result;        };        // вызов метода из интерфейса        System.out.println("Lambda reversed = " + ref.reverse("Lambda"));    }}

Результат выполнения:

Lambda reversed = adbmaL

Параметризированный функциональный интерфейс


До этого момента, мы использовали функциональные интерфейсы, которые принимали только один тип значения. Например:

@FunctionalInterfaceinterface MyInterface {    String reverseString(String n);}

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

Пример 5: параметризированный интерфейс и лямбда-выражения

// Параметризированный интерфейс@FunctionalInterfaceinterface GenericInterface<T> {    // параметризированный метод    T func(T t);}public class Main {    public static void main( String[] args ) {        // Объявление ссылки на параметризированный интерфейс        // который принимает String        // и присвоение ей лямбды        GenericInterface<String> reverse = (str) -> {            String result = "";            for (int i = str.length()-1; i >= 0 ; i--)            result += str.charAt(i);            return result;        };        System.out.println("Lambda reversed = " + reverse.func("Lambda"));        // Объявление ссылки на параметризированный интерфейс        // который принимает Integer        // и присвоение ей лямбды        GenericInterface<Integer> factorial = (n) -> {            int result = 1;            for (int i = 1; i <= n; i++)            result = i * result;            return result;        };        System.out.println("factorial of 5 = " + factorial.func(5));    }}

Результат выполнения:

Lambda reversed = adbmaLfactorial of 5 = 120

В этом примере, мы создали параметризированный функциональный интерфейс GenericInterface, который содержит параметризированный метод func().

Затем, внутри класса Main:

  • GenericInterface<String> reverse создает ссылку на интерфейс, который работает со String.
  • GenericInterface<Integer> factorial создает ссылку на интерфейс, который работает с Integer.

Лямбда-выражения и Stream API


В JDK8 добавлен новый пакет java.util.stream, который позволяет java-разработчикам выполнять такие операции, как поиск, фильтрация, сопоставление, объединение или манипулирование коллекциями, к примеру Lists.

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

Для этого мы можем использовать комбинацию Stream API и лямбда-выражений.

Пример 6: использование лямбд в Stream API

import java.util.ArrayList;import java.util.List;public class StreamMain {    // объявление списка    static List<String> places = new ArrayList<>();    // заполнение данными    public static List getPlaces(){        // добавление страны и города        places.add("Nepal, Kathmandu");        places.add("Nepal, Pokhara");        places.add("India, Delhi");        places.add("USA, New York");        places.add("Africa, Nigeria");        return places;    }    public static void main( String[] args ) {        List<String> myPlaces = getPlaces();        System.out.println("Places from Nepal:");                // Фильтрация городов        myPlaces.stream()                .filter((p) -> p.startsWith("Nepal"))                .map((p) -> p.toUpperCase())                .sorted()                .forEach((p) -> System.out.println(p));    }}

Результат выполнения:

Places from Nepal:NEPAL, KATHMANDUNEPAL, POKHARA

В приведенном выше примере обратите внимание на это выражение:

myPlaces.stream()        .filter((p) -> p.startsWith("Nepal"))        .map((p) -> p.toUpperCase())        .sorted()        .forEach((p) -> System.out.println(p));

Здесь мы используем такие методы, как filter(), map(), forEach() из Stream API, которые могут принимать лямбды в качестве параметра.

Также, мы можем описать собственные выражения на основе синтаксиса, описанного выше. Это позволит нам уменьшить количество строк кода.
Подробнее..
Категории: Java , Java 8 , Lambda expressions , Stream api

Из песочницы Мой путь к получению Oracle Certified Associate и Oracle Certified Professional

16.11.2020 20:18:08 | Автор: admin
Всем привет, меня зовут Руслан. Я работаю в крупном банке на должности team lead'a.

Хочу поделиться с вами моим опытом получения заветных званий Oracle Certified Associate, Java SE 8 Programmer (далее OCA) и Oracle Certified Professional, Java SE 8 Programmer (далее OCP).

image

Обновленный бейдж Oracle Certified Associate
image

Обновленный бейдж Oracle Certified Professional
image

Да, я знаю, что на Хабре уже есть достаточное количество статей на эту тему: раз, два, три, четыре. На мой взгляд, они хороши, но не отвечают на главный вопрос, возникший у меня уже после того, как я начал готовиться к первому экзамену OCA. Ответа на этот животрепещущий вопрос я так и не смог найти а зачем оно мне надо с точки зрения разработчика, какие профиты кроме бейджа и электронного сертификата на сайте я получу? Не будет ли это пустой тратой денег и времени?

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

Итак, мой бэкграунд на момент начала подготовки к экзамену:

  1. Прочтенная книга Философия Java авторства Брюса Эккель
  2. Около 1,5 лет работы с Java
  3. Базовые знание ООП и многопоточного программирования

На работе возникла некоторая стабильность, когда нет срочных и горячих задач, рутина начинает затягивать. Я находился в некотором расслабленном состоянии в плане сроков сдачи и дедлайнов. Вот тут мне и захотелось добавить какого-то перца. Про сертификацию в компании, где я работаю, вокруг то и дело возникали разговоры на эту тему, но это оставалось только разговорами и ни у кого руки до сдачи так и не дошли. Так бы жизнь шла спокойно и размеренно, пока судьба не столкнула меня с одним из лекторов Luxsoft'a, подтолкнувшим меня в пучину обучения.

image

Лектор, по счастливому стечению обстоятельств, является автором книги Nailing 1Z0-808: Practical Guide to Oracle Java SE8 Programmer I Certification по подготовке ОСА. Подготовка к первому экзамену (ОСА) заняла около месяца, практически все свободное время я проводил с книгой в руках либо за тренажером enthuware.

Процесс сдачи довольно прост:

  • вы бронируете себе экзамен в одном из сертифицированных центров в одном из городов на выбор, в моем случае Москва
  • платите порядка 150$
  • отправляетесь на экзамен в назначенное время.

Экзамен проходит в отдельной комнате, с кучей камер направленных на вас. Идею списать отбросьте сразу, на мой взгляд, это просто нереально. Время ограниченно, но для ОСА я считаю его достаточным при достаточном уровне подготовки. Результат вы узнаете довольно быстро, я, например, получил письмо на e-mail спустя 30-40 минут. И ура! Первый экзамен сдан с приличным результатом в 94%

Результат ОСА
image

Промежуточные итоги после сдачи ОСА, с точки зрения работающего разработчика. На первый взгляд некоторые темы затронутые в процессе сертификации кажутся игрушечными, но недооценивать их нельзя. Я считаю, что это те самые тонкости, которые отделяют одних разработчиков от других. Да, все из нас наверняка умеют создавать классы\интерфейсы, пользоваться наследованием и писать if statement. Но лишь малый процент людей заглядывает под капот инструмента, с которым работает. Все, чему я научился в рамках подготовки и сдачи ОСА, тут же начал распространять среди членов моей команды. Нет, я не кичился этим сертификатом, задирая свой нос, я никому ничего не сказал, а просто начал применять полученные знания и делиться ими. Без преувеличения, я бы сравнил это с курсами повышения квалификации.

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

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

Книги, которые я использовал:

  1. Java I/O, NIO and NIO.2
  2. OCP: Oracle Certified Professional Java SE 8 Programmer II Study Guide: Exam 1Z0-809
  3. OCP Java SE 7 Programmer II Certification Guide: Prepare for the 1ZO-804 exam

Ну и куда же без тренажeра enthuware.

В этот раз подготовка заняла 2,5 месяца. Я также почти все свободное время проводил либо за книгой, либо за тренажером. В ОСР темы более сложные и рассматриваются куда намного глубже, чем в ОСА. Повторяем процедуру регистрации, платим 150$ и едем сдавать. Я ради интереса выбрал другой центр для сдачи. На удивление помещение было почти таким же с той же кучей камер. В этот раз времени не хватало катастрофически, фрагменты, которые надо было прочесть, стали больше + усложнилась сама логика, которой надо было следовать. Несмотря на мою активную подготовку, я еле-еле успел ответить на все вопросы и сделать небольшое ревью. Как и в прошлый раз, ответ пришел в течение 30-40 минут. В этот раз результат был ниже, но я все равно считаю его достойным 85%.

Результат ОСР
image

Итак, что получилось в сухом остатке, времени на подготовку и сдачу я затратил около 4 месяцев, 300$ за сами экзамены + покупка книг и две лицензии на тренажер enthuware. Большие ли это затраты времени и денег в сравнении с полученными знаниями? Мой ответ нет. В процессе этого обучения мне удалось понять принцип работы Stream API, какой-то процент работы с многопоточностью и многое-многое другое. Можно ли было это все выучить, не сдавая никаких экзаменов, не покупая книг и прочего? Тут я отвечу да, но все не так однозначно. На личном примере могу сказать, что обучение вне временных рамок не так эффективно.

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

И отвечая на тот самый вопрос, который мучал меня, стоит ли оно того и что я приобрету в итоге. Я могу сказать, что стоит как минимум попробовать свои силы в сдаче ОСА. Кто-то из вас сможет взглянуть на давно уже ставшие привычными вещи с абсолютно другой стороны. Ну и конечно, в некоторых компаниях наличие сертификата даже первого уровня станет отличным поводом попросить прибавку.

Конструктивная критика приветствуется, буду рад фидбэку. У меня есть еще несколько тем, которыми я готов поделиться с вами: как попал на работу в этот крупный банк, имея около нулевые знания Java или, как спустя время, стал лидом команды, сам того не прося и не особо желая, или как по воле случая выступил на конференции Кодишь.
Подробнее..

Книга Система модулей Java

23.11.2020 16:15:20 | Автор: admin
image Привет, Хаброжители! Создать надежное и безопасное приложение гораздо проще, если упаковать код в аккуратные блоки. Система модулей в Java представляет собой языковой стандарт для создания таких блоков. Теперь вы можете контролировать взаимодействия различных JAR и легко обнаруживать недостающие зависимости. Фундаментальные изменения архитектуры затронули ядро Java, начиная с версии 9. Все API ядра распространяются в виде модулей, а для библиотек, фреймворков и приложений аналогичный подход можно считать хорошей практикой и рекомендацией.

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

Стратегии миграции и модуляризации


  • Подготовка к переходу на Java 9 и выше.
  • Непрерывная интеграция изменений.
  • Постепенная модуляризация проектов.
  • Генерация декларации модуля с помощью JDeps.
  • Взлом сторонних JAR-файлов с помощью инструмента jar.
  • Публикация модульных JAR для Java 8 и старше.

В главах 6, 7 и 8 обсуждаются технические подробности перехода на Java 9+ и превращения существующей кодовой базы в модульную. В этой главе мы более подробно рассмотрим, как наилучшим образом объединить эти подробности в успешные усилия по миграции и модуляризации. Сначала обсудим, как провести постепенную миграцию, которая хорошо согласуется с процессом разработки, особенно с инструментами сборки и непрерывной интеграцией. Далее рассмотрим, как использовать безымянный модуль и автоматические модули в качестве строительных блоков для конкретных стратегий модуляризации. И наконец, разберем варианты превращения JAR в модульные их самих или их зависимостей. К концу этой главы вы не только поймете механизмы, стоящие за миграционными проблемами, но и узнаете, как наилучшим образом использовать их в своих целях.

9.1. Стратегии миграции


Благодаря знаниям, полученным в главах 6 и 7, вы стали готовыми к любому бою, в который Java 9+ может втянуть вас. Теперь пришло время расширить кругозор и разработать более масштабную стратегию. Как лучше организовать фрагменты, чтобы сделать миграцию максимально тщательной и предсказуемой? В этом разделе приведены рекомендации по подготовке к миграции, оценке усилий по ее проведению, настройке непрерывной сборки на Java 9+ и преодолению недостатков параметров командной строки.

ПРИМЕЧАНИЕ
Многие темы в данном разделе связаны с инструментами сборки, но они достаточно универсальны, поэтому вам не требуется знание какого-либо конкретного инструмента. В то же время я хотел поделиться своим опытом с Maven (единственным инструментом сборки, который я использовал на Java 9+), поэтому иногда упоминал функцию Maven, применяемую мной для выполнения определенных требований. Я не буду вдаваться в подробности, поэтому вам самим придется выяснить, как работают все эти функции.

9.1.1. Подготовительные обновления


Итак, если вы еще не используете Java 8, то советую тотчас обновить приложение до этой версии! Сделайте себе одолжение и не переходите сразу на две или более версии Java. Обновитесь, приведите в действие все инструменты и процессы, на некоторое время запустите приложение, а затем приступайте к следующему обновлению. Те же действия подойдут, если нужно обновиться с Java 8 до 11, делайте это по одному шагу за раз. Если возникнут какие-либо проблемы, то вы действительно захотите узнать, какая версия Java или обновление зависимостей вызвали их.

Говоря о зависимостях, еще одно действие, которое можно сделать, даже не принимая во внимание Java 9+, это начать обновлять и их, и инструменты. Помимо общих преимуществ обновления, можно непреднамеренно обновить версию, в которой есть проблемы с Java 9+, до версии, прекрасно с ней совместимой. Вы даже не заметите, что проблема существовала. Если версии, совместимой с Java 9+, еще нет, то обновление зависимости или инструмента по-прежнему упростит процесс перехода после публикации совместимой версии.

Обзор охвата с AdoptOpenJDK
AdoptOpenJDK, сообщество членов групп пользователей, разработчиков и поставщиков Java, которые являются сторонниками OpenJDK, предоставляет список различных проектов с открытым исходным кодом и их совместимостью с последней и следующей версиями Java: mng.bz/90HA.

9.1.2. Оценка усилий


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

Поиск проблем

Вот наиболее очевидный порядок действий для составления списка проблем.

  • Настройте сборку для компиляции и тестирования на Java 9+ (Maven: набор инструментов) в идеале таким образом, чтобы можно было собирать все ошибки вместо того, чтобы останавливаться на первой (Maven: --fail-never).
  • Запустите всю сборку на Java 9+ (Maven: ~/.mavenrc), снова собирая все ошибки.
  • Если вы разрабатываете приложение, то создайте его как обычно (имеется в виду еще не в Java 9+), а затем запустите в Java 9+. Используйте --illegal-access=debug или deny для получения дополнительной информации о несанкционированном доступе.

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

Рекомендуется применить несколько быстрых исправлений, таких как добавление экспорта или модулей JEE. Это позволит заметить более сложные проблемы, которые могут скрываться за доброкачественными. На данном этапе ни одно исправление не является слишком быстрым или слишком грязным все, что заставляет сборку выдать новую ошибку, считается победой. Если выводится слишком много ошибок компиляции, то можно скомпилировать проект на Java 8 и просто запустить тесты на Java 9+ (Maven: mvn surefire:test).

Затем запустите JDeps для проекта и зависимостей. Проанализируйте зависимости от внутренних API JDK (см. подраздел 7.1.2) и запишите все модули JEE (раздел 6.1). Кроме того, обратите внимание на разделение пакетов между платформенными модулями и JAR-файлами приложения (см. подраздел 7.2.5).

Наконец, найдите в кодовой базе вызовы AccessibleObject::setAccessible (см. подраздел 7.1.4), приведения к TOURLClassLoader (раздел 6.2), анализ системных свойств java.version (см. подраздел 6.5.1) или напрямую вписанные в код URL ресурсов (см. раздел 6.3). Поместите все, что нашли, в один большой список теперь пришло время проанализировать его.

Насколько все плохо?

Найденные проблемы должны быть разделены на две категории: Я видел подобное в этой книге и Что, & *1 #?, происходит?. Далее разделите проблемы из первой категории на Имеет по крайней мере временное решение и Это сложная проблема. Особо сложные проблемы удаленные API и разделения пакетов между платформенными модулями и JAR, которые не реализуют утвержденный стандарт или самостоятельную технологию.

Важно не путать распространенность с важностью! Вы можете получить около тысячи ошибок, поскольку отсутствует модуль JEE, но это можно с легкостью исправить. С другой стороны, у вас большие проблемы, если основная функция зависит от приведения загрузчика классов приложения к URLClassLoader. Или же может присутствовать критическая зависимость от удаленного API, но, поскольку вы хорошо спроектировали свою систему, это просто вызывает несколько ошибок компиляции в одном подпроекте.

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

К концу анализа у вас должен получиться список проблем в этих трех категориях:

  • известная проблема с легким решением;
  • известная сложная проблема;
  • неизвестная проблема, требующая изучения.

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

Об измерении оценки

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

Миграция к Java 9+ ставит вас лицом к лицу с музыкой давно принятых решений. Проект может быть тесно связан с устаревшей версией веб-фреймворка, который нужно было обновить еще несколько лет назад, или же он накопил большой технический долг вокруг несопровождаемой библиотеки. И, к сожалению, оба из них перестают работать в Java 9+. Что нужно сделать сейчас это погасить некоторые технические долги, и, как всем известно, оценить пени и проценты будет весьма непросто. Наконец, точно так же, как в старой доброй битве с боссом, критическая проблема та, которая стоит дороже всех, может быть скрыта за несколькими другими нарушителями спокойствия, поэтому ее нельзя увидеть, не подобравшись вплотную. Я не говорю, что эти сценарии очень вероятны, просто они возможны, поэтому будьте осторожны, гадая, сколько времени может уйти на переход к Java 9.

9.1.3. Непрерывная интеграция в Java 9+


Учитывая предположение, что вы постоянно разрабатываете проект, следующим шагом будет создание успешной сборки Java 9+. Есть много решений, требующих принятия.

  • Какую ветку нужно собрать?
  • Нужна ли отдельная версия?
  • Как разделить сборку, если она не может полностью работать на Java 9+ с первого дня?
  • Как поддерживать параллельные сборки Java 8 и Java 9+?

В конце концов, нужно найти ответы, соответствующие вашему проекту и настройке непрерывной интеграции (НИ). Позвольте поделиться идеями, которые хорошо работали во время моей собственной миграции, вы можете объединить их так, как вам нравится.

Какую ветку собирать

У вас может возникнуть соблазн настроить собственную ветку для миграции и позволить НИ-серверу создать данную ветку на Java 9+, а остальные на Java 8, как и раньше. Но миграция может занять некоторое время, что приведет к долгоживущей ветке, и я обычно стараюсь не делать это по разным причинам:

  • вы сами по себе и ваши изменения не находитесь под постоянным контролем команды, которая работает на их основе;
  • обе ветки могут претерпеть множество изменений, что увеличивает вероятность конфликтов при обновлении или объединении ветки Java 9+;
  • если для того, чтобы продлить изменения в основной ветке разработки к ветке Java 9+, потребуется некоторое время, то остальная часть команды тогда же может свободно добавлять в нее код, который создаст новые проблемы в Java 9+ при отсутствии немедленной обратной связи.

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

Какую версию собирать

Должна ли сборка Java 9+ создавать отдельную версию артефактов что-то вроде -JAVA-LATEST-SNAPSHOT? Вслед за решением создать отдельную ветку Java 9+, вероятно, придется создавать отдельную версию. В противном случае можно легко смешать артефакты снимков файловой системы из разных веток, что нарушает сборку тем чаще, чем больше веток отклоняется. Если вы решили проводить сборку из основной ветки разработки, то создание отдельной версии может быть непростым, но я никогда не пытался это делать, поскольку не нашел на то веских причин.

Независимо от способа работы с версиями, при попытке заставить что-то работать на Java 9+ вы, вероятно, иногда будете собирать один и тот же подпроект с одной и той же версией на Java 8. Единственное, что я делаю снова и снова, даже если решаю не делать, устанавливаю артефакты, собранные с помощью Java 9+, в локальный репозиторий. Знаете, тот самый рефлекторный mvn clean install?

Это не очень хорошая идея: тогда нельзя будет использовать эти артефакты в сборке Java 8, поскольку она не поддерживает байт-код Java 9+.

При локальной сборке на Java 9+ старайтесь не устанавливать артефакты! Я для этого использую mvn clean.

Что собирать на Java 9+

Конечная цель запустить инструмент сборки на Java 9+ и собрать все проекты на всех этапах/задачах. В зависимости от того, сколько элементов в данном списке вы создали ранее, возможно, потребуется всего лишь изменить парочку вещей, чтобы этого достичь. В таком случае выполните данные действия не нужно усложнять процесс. С другой стороны, если список более сложный, то разделить сборку Java 9 можно несколькими способами:

  • запустить сборку на Java 8 и только компилировать и тестировать на Java 9+. Я поговорю об этом чуть позже;
  • провести миграцию для каждой цели/задачи. Это значит, что сначала нужно попытаться скомпилировать весь проект с помощью Java 9+, прежде чем запускать тесты;
  • провести миграцию по подпроекту, то есть сначала попытаться скомпилировать, протестировать и упаковать один подпроект, прежде чем переходить к следующему.

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

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

1. Собрать все на Java 8.
2. Собрать все на Java 9+, кроме проблемных подпроектов (подпроекты, зависящие от них, были собраны на основе артефактов Java 8).

Более подробно с книгой можно ознакомиться на сайте издательства
Оглавление
Отрывок

Для Хаброжителей скидка 25% по купону Java

По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru