среда, 11 октября 2017 г.

Инициализация базы данных в Docker

Если нам необходима готовая база данных при старте посгрешки в докере, это возможно достичь, добавив следующие строки в dockerfile:

ENV INIT_SQL init.sql
ADD init.sql /docker-entrypoint-initdb.d/

Они копируют init.sql в докер контейнер. Выполнение данного скрипта произойдет только один раз, при мервом запуске.

воскресенье, 24 сентября 2017 г.

Запуск Postgres в Docker

Конфигурация docker файла:
FROM library/postgres
ENV POSTGRES_USER postgres
ENV POSTGRES_PASSWORD postgres
ENV POSTGRES_DB postgres

Для того, чтобы собрать контейнер, в консоли выполняем
sudo docker build  -t postgres .

В результате в консоль будет выводится процесс сборки контейнера, а в конце будет указан его идентификатор:
Этим идентификатором и воспользуемся для запуска:
sudo docker run 1f66d03cb1e7 & 
& используется для запуска в бэкграунде.  Выполнив:
sudo docker ps -a

Мы увидим, что наш контейнер запущен:

Чтобы понять, какой адрес у запущенной базы данных воспользуемся командой:
sudo docker inspect 0ba3681d3a3d
В консоль будет выведена вся информация о контейнере, в ней будет указан ip адрес, по нему и можно обращаться к базе:

понедельник, 18 сентября 2017 г.

Установка Docker на Ubuntu

Рассмотрим процесс установки docker, в Ubuntu, я использовал версию 17.04.
Первым делом обновим базу пакетов:
sudo apt-get update

Далее добавим ключ официального репозитория Docker в систему:
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D

Добавим репозиторий Docker в список источников, для утилиты apt:
sudo apt-add-repository 'deb https://apt.dockerproject.org/repo ubuntu-xenial main'

Обновим информацию о пакетах из добавленного репозитория:
sudo apt-get update

Убедимся, что собираемся установить Docker из репозитория Docker, а не из репозитория по умолчанию Ubuntu:
apt-cache policy docker-engine

Результат должен быть похож на этот:
Установим Docker:
sudo apt-get install -y docker-engine

Проверим, запущен ли, демон:
sudo systemctl status docker

В результате получим:
Ну и запустил hello world:
sudo docker run hello-world

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

суббота, 12 августа 2017 г.

Синглтон: глубокое погружение

Сингтон - паттерн проектирования, который используется, чтобы контролировать число создаваемых объектов, слодовательно он относится семейству паттернов Создатели.
Была тенденция создавать максимум один объект, но в некоторых ситуациях необходимо создать фиксированное число объектов; и именно в этом нам поможет данный паттерн. Обычно, конструктор создается приватным, чтобы убедиться, что внешний мир не может создать объекты и предоставляем один статический метод, который возвращает объект. Создание объекта происходит только если он не был создан до этого.
Со временем люди осознали несколькоп роблем с обычной реализацией синглтона и он был улучшен, чтобы решить эти проблемы. Запомните, что синглтон не хороший или плохой, он просто может подходить для решения вышей проблемы.
В этой статье мы рассмотрим различные реализации синглтонов.
Взглянем на диаграмму класса:

Применение синглтона

Есть несколько ситуаций, когда целесобразно использовать паттерн синглтон. На пример: логирование, кэширование, балансировка нагрузки, конфигурирование, коммуникации и пулл-коннекшинов в БД. Пример использования синглтона в Java API - класс Runtime.

Реализация синглтона

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

public class MySingleton {
     private static final MySingleton mySingleton = new MySingleton();
     private MySingleton(){}     
     public static MySingleton getInstance(){
        return mySingleton;
     }
}
А вот реализация использующая инициализацию при первом обращении. В многопоточном приложении данная реализация будет плогих решением:

class MySingleton {
     private static MySingleton mySingleton;
     private MySingleton(){}
     public static MySingleton getInstance(){
           if(null == mySingleton) {
                 mySingleton = new MySingleton();
           }
           return mySingleton;
     }
}
Решение для многопоточной среды должо избежать гонки состояний  и убедиться, что не будет нарушения философии синглтона. Но в примере ниже, делать весь метод synchronized - не лучший подход, т.к нам необходимо добавить ограничение только на создание объекта.

class MySingleton {
     private static MySingleton mySingleton;
     private MySingleton(){}
     public synchronized static MySingleton getInstance(){      
        if(null == mySingleton) {
           mySingleton = new MySingleton();
        }
        return mySingleton;
     }
}
Пример ниже делает все то же, но так же пользуется двойной проверкой, используя блокировку по объекту. Это реализация гарантирует потокобезопасность. Но дополнительный объект, который нужен только для блокировки - не лушчая практика.

class MySingleton {
  private static MySingleton mySingleton;
  private static final Object lock = new Object();
  private MySingleton(){}
  public static MySingleton getInstance(){
     if(null == mySingleton) {
         synchronized(lock) {
            if(null == mySingleton) {
               mySingleton = new MySingleton();
            }
         }   
     }
     return mySingleton;
  }
}
Другая многопоточная реализация,  пользуется двойной проверкой, но с блокировкой на уровне класса. Отметив объект MySingletone, как volatile, мы уверены, что изменения , сделанные в одном потоке, будут видны другим потокам. Данная реализация гарантирует потокобезопасность.

class MySingleton {
    private volatile static MySingleton mySingleton;
    private MySingleton() {}
    public static MySingleton getInstance() {
        if (null == mySingleton) {
            synchronized(MySingleton.class) {
                if (null == mySingleton) {
                    mySingleton = new MySingleton();
                }
            }
        }
        return mySingleton;
    }
}
Данная реализация предоставляет конструктор, который помешает нарушить контракт синглтона, используя рефлексию.

class MySingleton {
    private volatile static MySingleton mySingleton;
    //Reflection может получить доступ к приватному конструктору
    private MySingleton() throws Exception {
        if (null == mySingleton) {
            mySingleton = new MySingleton();
        } else {
            throw new Exception("Это singleton; не ожидается создание более одного экземпляра");
        }
    }
    public static MySingleton getInstance() throws Exception {
        if (null == mySingleton) {
            synchronized(MySingleton.class) {
                if (null == mySingleton) {
                    mySingleton = new MySingleton();
                }
            }
        }
        return mySingleton;
    }
}
Очень популярная реализация, с использованием статического класса, которая предоставляет ленивую инициализацию (при первом обращении) и потокобезопасность.

public class MySingleton {
    private MySingleton() {}
    private static class SingletonUisngInner {
        private static MySingleton mySingleton = new MySingleton();
    }
    public static MySingleton getInstance() {
        return SingletonUisngInner.mySingleton;
    }
}
В некоторых обстоятельствах, если ваш синглтон наследуется от Cloneable - вашему классу необходимы дополнительные проверки, чтобы избежать нарушения контракта синглтона. Вам необходимо переопределить методо clone  и явно выкинуть ошибку CloneNotSupportedException.

class ClonedClass implements Cloneable {
    //Некоторая логика
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class MySingleton extends ClonedClass {
    private MySingleton() {}
    private static class SingletonUisngInner {
        private static MySingleton mySingleton = new MySingleton();
    }
    public static MySingleton getInstance() {
        return SingletonUisngInner.mySingleton;
    }
    public Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
}
Следующий и наш финальный, очень популярный и умный способ реализации синглтона - использовать Enum, который возьмет все заботы на себя.

public enum  EnumSingleton{
    INSTANCE;
}
Иногда, люди говорят о синглтонах в нескольких JVM - разберемся с этим. Синглтон означет, что есть только один объект, и мы хоршо знаем, что жизненным циклом объекта управляет JVM, так что один разделяемый объект, среди нескольких JVM невозможен.
Но если вам это необходимо, вы можете создать объект в одной JVM и передавать его через сериализацию, в другие JVM (но помните, что вы десириализуете его, так что, все что отмечено static или transient не передается, и где то контракт синглтона может быть нарушен). Вы так же можете использовать RMI объекты, как синглтоны, чтобы удовлетворить вашим потребностям.
Веселого обучения!
PS это мой перевод данной статьи

пятница, 11 августа 2017 г.

Что такого плохого в Синглтонах

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

Чтобы было более понятно, этот паттерн содержит класс, который отвечает за создание объекта, а так же удостоверяется, что создается только один объект.

class Singleton {
    private static Singleton INSTANCE = null;
    private String criticalData;
    private Singleton() {
        criticalData = "Это должно быть уникальным, а состояние универсальным";
    }
    synchronized static Singleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton();
        }
        return INSTANCE;
    }
    public String getString() {
        return this.criticalData;
    }
    public void setCriticalData(String value) {
        criticalData = value;
    }
}
Теперь мы знаем, что из мебя представляе класс синглтон и как он  работает. Итак, в чем проблема? Подсказка: самая очевидная проблема находится в 7 строке.
Т.к. блок synchronized может быть доступен только одному потоку, это может создать узкое место при получении объекта. Но есть и другие менее известные проблемы.

Синглтоны и состояние

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

Создается один объект

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

Сингтоны предоставляют глобальное состояние

Именно поэтому у нас есть синглтоны. Да. Но какой ценой? (Глобальные переменные были плохими! Помните?) Это обеспечивает возможность для некоторого сервиса в вашем приложении, так что нам не нужно передавать ссылку на этот сервис. Желание создать что-то глобальное, чтобы избежать его распространение, - это запашок от вашего дизайна.

PS это мой перевод данной статьи

пятница, 28 июля 2017 г.

Код без NPE и без проверок на NULL

Первый: Ты действительно уверен, что эта переменная никогда не будет null
Второй: конечно!
NullPointerException 

Ведь так начинается Java код-ревью? NPE всегда является кошмаром для Java разработчиков. Рассмотрим не самый простой пример кода:

public interface Service {  
    public boolean switchOn(int timmer);
    public boolean switchOff(int timmer);
    //другие элементы
}
public class RefrigeratorService implements Service {
// ...
}
public class HomeServices {
    private static final int NOW = 0;
    private static HomeServices service;
    public static HomeServices get() {
        //проверка на Null #1
        if(service == null) {
            service = new HomeServices();
        }
        return service;
    }
    public Service getRefrigertorControl() {
        return new RefrigeratorService();
    }
    public static void main(String[] args) {
        /* получаем Home Services */
        HomeServices homeServices = HomeServices.get();
        //проверка на Null #2
        if(homeServices != null) {
            Service refrigertorControl = homeServices.getRefrigertorControl();
            //проверка на Null #3
            if (refrigertorControl != null) {
                refrigertorControl.switchOn(NOW);
            }
        }
    }
}
Как видно вышел, в коде несколько проверок на Null. Конечно все это сделано, чтобы код был более надежным во всех ситуация. Это необходимо, или ...

Есть путь лучше?

В общем - да! В Java 8 представили java.util.Optional<T>. Это контейнер, который может содержить или не содержить null значения. Java 8 предоставляет нам более безопасный способ  взаимодействовать с объектами, которые  в некоторых случаях могут быть null. Данное решение вхоновлено идеями Haskell  и Scala.
Если коротко - класс Optional включает методы, для корректной работы в двух случаях, когда значение существует и когда отсутствует. Преимущество, в сравнении с null проверкой, заключается в том, что класс Optional, заставляет обдумывать случаи, когда значение отсутствует. Т.о.  получится избежать непредвиденных NPE.
В примере выше у нас есть фабрика HomeService, которая обрабатывает несколько устройств, доступных в доме. Но эти устройства функционально могут быть досутпны или не доступны. Т.е. может случиться NPE. Вместо того, чтобы добавлять проверку на null, обернем объект в Optional.

Оборачивание в Optional<T>

Рассмотрим получение объекта из фабрики. Вместо возвращение инстанса элемента, обернем его в Optional. Это позволит пользователю API, понимать, что возвращаемый сервис и может быть функционально доступным или нет. Используем безопасно:

public Optional<Service> getRefrigertorControl() {
    Service s = new  RefrigeratorService();
    //...
    return Optional.ofNullable(s);
}
Как вы видите, Optional.ofNullable предоставляет простой путь  получить обернутый объект. Есть другие пути, чтобы получить оберный объект в Optional: Optiona.empty() или Optional.of(). Первый возвращает пустой объект, вместо null,  а второй - оборачивает не null значения объектов.

Как это помогает избежать null проверок?

Обернув объект в Optional единожды, получаем множество полезных методов для вызова функционала обернутого объекта, без NPE.

Optional ref = homeServices.getRefrigertorControl(); 
ref.ifPresent(HomeServices::switchItOn);
Optiona.ifPresent вызывает потребителя (Consumer) по ссылке, если значение не null, иначе ничего не делает. Это очень красиво и легко для понимания. В примере выше, HomeService.switchOn(Service) вызывается, только если Optional содержить не null значение.
Мы часто используем тернарный оператор, чтобы проверить на null и значение по умолчанию, если null. Optional предоставляет то же самое без проверок. Optional.orElse(defaultObj) возвращает defaultObj, если в Optional значение null. Используем это в нашем коде:

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}
Сейчас HomeServices.get() делает то же самое, но лучшим способов. Он проверяет, проинициализирован ли сервис. Optional<T>.orElse(T) помогает вернуть значение по умолчанию.
В итоге вот наш код без NPE и null проверок:

import java.util.Optional;
public class HomeServices {
    private static final int NOW = 0;
    private static Optional<HomeServices> service;
    public static Optional<HomeServices> get() {
        service = Optional.of(service.orElse(new HomeServices()));
        return service;
    }
    public Optional<Service> getRefrigertorControl() {
  if(ServiceDiscovery.isAvaiable("refrigetor")) {
   Service s = RefrigeratorService.get();
   return Optional.ofNullable(s);
  }
  return Optional.empty();
 }
    public static void main(String[] args) {
        Optional<HomeServices> homeServices = HomeServices.get();
        if(homeServices.isPresent()) {
            Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
            refrigertorControl.ifPresent(HomeServices::switchItOn);
        }
    }
    public static void switchItOn(Service s){
        //...
    }
}

PS: это мой перевод данной статьи

суббота, 1 июля 2017 г.

Spring boot. Spring Security

Рассмотрим возможности Spring Security. В качестве примера, воспользуемся веб-проектом из предыдущих статей. Первым делом, необходимо добавить зависимость Spring Security в наш pom-файл:
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
Собственно, этого остаточно, для самого простого примера. Если запустить наш пример и посмотреть на консоль вывода, можно заметить следующее сообщение:

Данный пароль можно использовать, если обратиться к localhost:8080:
Попробуем немного усложнить пример и создадим application.properties файл с таким содержимым:
security.user.name = user
security.user.password = password
Теперь, запустив наш проект, мы не увидим в консоли вывод пароль, в данном случае нам подойдут данные, которые мы указали в проперти файле.

Далее, рассмотрим In-memory security. Для этого необходимо создать следующий класс:
package com.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableGlobalAuthentication
public class InMemorySecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("user").password("password").
                roles("USER")
                .and().withUser("admin").password("password").
                roles("USER", "ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/journal/**").authenticated()
                .and()
                .httpBasic();
    }
}

В данном классе:

  • Aннотация EnableGlobalAuthentication - говорит о том, что класс с данной аннотацией используется для конфигурирования AuthenticationManagerBuilder.
  • AuthenticationManagerBuilder инжектится благодаря аннотации Autowired
  • WebSecurityConfigurationAdapter - абстрактный класс, предоставляющий удобную базу для конфигурации инстанса WebSecurityConfigurer
  • configure - переопределенный метод, для настройки HttpSecurity. В данном случае, достп по адресу "/" предоставляется свободный, а для /journal/** - необходима авторизация