Случайны выбор дневника Раскрыть/свернуть полный список возможностей


Найдено 17060 сообщений
Cообщения с меткой

java - Самое интересное в блогах

Следующие 30  »
rss_rss_hh_new

[Из песочницы] Как начать пользоваться Swing GUI-визардом IntelliJ IDEA. Подробная инструкция

Вторник, 19 Июля 2016 г. 17:44 (ссылка)

Давно не писал настольных приложений на Java вообще и с использовании Swing в частности. Однако, возникла необходимость немного по GUIть. В качестве инструмента выбрал IntelliJ IDEA Community edition, 2016.1 версии.



Взялся ваять и, естественно, первое, на что налетел — хотя со времён Borland Java Builder 2006 воды утекло немало, экранные интерфейсы создавать проще не стало, скорее наоборот. А одной из причин выбора IDEA было как раз наличие Swing дизайнера «из коробки», однако как им пользоваться с ходу решительно непонятно — форма генерится, класс создаётся, создаются переменные контролов из дизайнера… но и только: при создании нашего класса форма на экране не появляется



Пошарил интернет, информации приблизительно ноль. Народ говорит, мол, «создавай и — вперёд!». Хм…



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



Создание Swing GUI форм средствами JetBrains IntelliJ IDEA 2016.1





Во-первых, для понимания процесса лучше начать с того. что зайти в меню IDEA «File -> Settings» — там «Editor -> GUI Designer» и установить флажок Generate GUI Into: в Java source code. (это немного поможет пониманию процесса на первом этапе — потом можно будет убрать обратно).



image



Далее открываем дерево исходного кода своего проекта и кликаем правой кнопкой мыши на любую папку или файл исходного кода java и выбираем «New -> Dialog» — вводим имя класса для формы.



image



В итоге нам действительно сгенерили класс-наследник JDialog (который можно создать и использовать) и форма к нему.

Запускаем наш проект на выполнение и… о ужасчудо! при компиляции IDEA добавляет в конец нашего файла некоторый дополнительный код.



    {
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
// DO NOT EDIT OR ADD ANY CODE HERE!
$$$setupUI$$$();
}

/**
* Method generated by IntelliJ IDEA GUI Designer
* >>> IMPORTANT!! <<<
* DO NOT edit this method OR call it in your code!
*
* @noinspection ALL
*/
private void $$$setupUI$$$() {
contentPane = new JPanel();
contentPane.setLayout(new com.intellij.uiDesigner.core.GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1));
....................
}

/**
* @noinspection ALL
*/
public JComponent $$$getRootComponent$$$() {
return contentPane;
}


Несложно догадаться, что вся наша Swing-овая форма конфигурируется в автогенерируемом методе $$$setupUI$$$.



Вспомните настройку, которую мы установили в самом начале — «GUI Into: -> Java source code». Если её не ставить, то этот метод просто будет появляться напрямую в _class_ файле, минуя java-файл (декомпилируйте его, если сомневаетесь — я это сделал). Соответственно, можете вернуть настройку «GUI Into:» к первоначальному виду, чтобы этот код (который всё равно редактировать настоятельно не рекомендуют) не мозолил глаза.



Теперь, когда мы поняли, как оно работает, перейдём к созданию прочих форм — необязательно диалогов.



Опять правой кнопкой мыши кликаем на папку или файл исходного кода, выбираем «New -> GUI Form» — вводим имя класса для формы.



Генерится класс и форма к нему. Накидываем на форму несколько контролов. В GUI дизайнере смотрим имя корневого элемента (обычно panel1, если IDEA не задала имя, а такое бывает, задайте принудительно — я для наглядности назвал rootPanel).



image



Переходим к исходному коду нашего класса.



Итак:

1. Добавляем для нашего класса наследование «extends JFrame»;

2. Добавляем конструктор класса со строками:



    setContentPane(panel1);
setVisible(true);


Итоговый код класса
public class GUIFrame extends JFrame{
private JButton button1;
private JPanel rootPanel;

public GUIFrame() {
setContentPane(rootPanel);
setVisible(true);

setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new GUIFrame();
}
}




Всё. Форма готова к употреблению. Остальное смотрите в многочисленных инструкциях по Swing.



P.S. Как вариант, можно не наследовать наш класс от JFrame, а создать конструктор вида:



    JFrame frame = new JFrame();
frame.setContentPane(panel1);
frame.setVisible(true);


Такой вариант тоже работает — возможно, кому-то пригодится.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/305974/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Дайджест интересных событий из мира Java, и вокруг нее #6 (04.07.2016 — 17.07.2016)

Понедельник, 18 Июля 2016 г. 22:59 (ссылка)

image



В этом выпуске



— JUnit 5 к нам приходит

— Сборка окончена: CMS будет удален из Java

— Java EE: Oracle прервал молчание

— В дебри JIT вместе с Azul

… и многое другое





1. Новости



1.1.Вышел первый майлстоун-релиз JUnit 5

Ссылка 1: http://www.swiftmind.com/de/2016/07/07/junit-5-0-0-m1-released/

Ссылка 2: http://www.codeaffine.com/2016/02/18/junit-5-first-look/



Разработчики обещают много приятных фич — поддержка лямбд, динамическую регистрацию тестов, и т.д… Более подробно можно почитать в официальной документации. Часть этого функционала присутствовала и в предыдущих версиях. Но API JUnit 4 далек от идеала. Наращивание функционала привело к накоплению технического долга, выражающегося в конкурирующих интерфейсах, запутанных сущностях, и прочих странностях. В новой версии API будет не только более продвинутым, но и более удобным, и целостным. Ждем.



1.2. Дни CMS сочтены

Ссылка 1: http://openjdk.java.net/jeps/291

Ссылка 2: http://mail.openjdk.java.net/pipermail/hotspot-gc-dev/2016-June/018353.html

Ссылка 3: http://mail.openjdk.java.net/pipermail/hotspot-gc-dev/2016-July/018532.html



Это не новость. Идея удалить CMS была материализована в виде JEP-а еще в 2015-м году. Oracle хочет целиком сфокусироваться на развитии G1. Однако сейчас разработчики вплотную подошли к началу выпиливания, что породило ряд вопросов.



Так, представители SAP и Google заявляют, что хотят продолжать развивать CMS в рамках своих сборок OpenJDK. Поэтому CMS нельзя просто взять и выпилить. Его хорошо бы либо абстрагировать, либо выключать его компиляцию каким-нибудь макросом. Но и здесь не все гладко, так как Oracle с большим скрипом пропускает пуши со стороны в кодовую базу OpenJDK.



Парням из Oracle все эти сложности нафиг не нужны. Идеальный вариант для них — просто грохнуть кодовую базу. Комментарий Mark Reinhold позволяет надеяться, что IT-гигант проявит определенную ответственность, и стороны смогут придти к компромиссу.



1.3. Тренды и тенденции в Java: большое исследование RebelLabs

Ссылка: http://zeroturnaround.com/rebellabs/java-tools-and-technologies-landscape-2016/



Ребята из ZeroTurnaround провели большой опрос Java-специалистов, и составили картину современных тенденций в мире Java. 75% разработчиков думают, что они лучше, чем в среднем по больнице. 3 из 5 попробовавших микросервисы на вкус, не поняли, зачем это все нужно. IDEA наконец обогнала Eclipse. И прочее, и прочее. Почитайте, крайне интересно.



1.4. Oracle рассказал о будущем Java EE

Ссылка: http://www.theregister.co.uk/2016/07/07/oracle_java_ee_8/



Если коротко: «отставить панику, ситуация под контролем, на JavaOne все расскажем». На самом деле, тема стенаний Java EE уже немного утомила. Поэтому возможно действительно стоит расслабиться, и просто дождаться сентября. А там, гляди, и «само рассосется». Хотя едва ли многие из вас сильно напрягались по этому поводу :-)



Тем временем Mark Little из RedHat немного прояснил ситуацию с MicroProfile. Это инициатива про стандарты, и не про стандарты. Про Java EE, и не про Java EE. Продвигать ее будут через JCP, и не через JCP. Короче говоря, за все хорошее, против всего плохого. Пока что выглядит слишком рыхло.



Кстати, еще одна хорошая новость — WebSphere теперь Java EE 7 compliant! Поздравляем коллег из IBM. Как говорится, дорога ложка к обеду.



2. Почитать



2.1. В дебри JIT вместе с Azul

Ссылка: https://www.youtube.com/watch?v=oH4_unx8eJQ







Шикарный доклад о внутренностях JIT от Douglas Hawkins из Azul Systems. Настоятельно рекомендовано к просмотру, если вы не присутствовали на самом ивенте, организованным JUG.RU.



2.2. О чем молчит G1

Ссылка: https://dzone.com/articles/understanding-g1-gc-log-format



У CMS земля горит под ногами. Поэтому самое время поднатореть в дебаге проблем G1, и разобраться, что он печатает в лог. Автор статьи достаточно сжато и лаконично рассказывает, как начать говорить с новым сборщиком мусора на одном языке.



2.3. Интервью с CEO JetBrains

Ссылка: http://www.rbc.ru/spb_sz/04/07/2016/577a35b09a79477fa19c6cb5



Всегда инетерсно почитать интервью с умным человеком. Просто оставлю это здесь.



3. Мудрость



3.1. Do Repeat Yourself

Sometimes duplicating things, either code or data, can significantly simplifies a system. DRY isn't absolute.

— John Carmack (@ID_AA_Carmack) 15 июля 2016 г.



3.2. Про тестирование

I'm beginning to believe that writing well-designed tests actually requires more technical skill than the code it tests.

— Dan Allen (@mojavelinux) 9 июля 2016 г.



3.3. Performance и элегантность

"The key to performance is elegance, not battalions of special cases." - Jon Bentley and Doug McIlroy

— Programming Wisdom (@CodeWisdom) 12 июля 2016 г.

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



4. Юмор



4.1. Все тлен

We designed distributed architectures, highly available and resilient systems, reactive interfaces & what are they for?
Catching Pokemons ;(

— Mario Fusco (@mariofusco) 16 июля 2016 г.



4.2. Антипаттерны

Reminder that some anti-patterns look impressive. pic.twitter.com/uMFuqeSYIg

— map perkins (@mapperkins) 12 июля 2016 г.





Предыдущие выпуски

#5 (20.06.2016 — 03.07.2016)

#4 (06.06.2016 — 19.06.2016)

#3 (23.05.2016 — 05.06.2016)
Original source: habrahabr.ru.

https://habrahabr.ru/post/305890/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

JIT-компилятор оптимизирует не круто, а очень круто

Понедельник, 18 Июля 2016 г. 22:12 (ссылка)

Недавно Лукас Эдер заинтересовался в своём блоге, способен ли JIT-компилятор Java оптимизировать такой код, чтобы убрать ненужный обход списка из одного элемента:



// ... а тут мы "знаем", что список содержит только одно значение
for (Object object : Collections.singletonList("abc")) {
doSomethingWith(object);
}


Вот мой ответ: JIT может даже больше. Мы будем говорить про HotSpot JVM 64 bit восьмой версии. Давайте рассмотрим вот такой простой метод, который считает суммарную длину строк из переданного списка:



static int testIterator(List list) {
int sum = 0;
for (String s : list) {
sum += s.length();
}
return sum;
}


Многим Java-программистам известно, что этот код полностью эквивалентен следующему:



static int testIterator(List list) {
int sum = 0;
Iterator it = list.iterator();
while(it.hasNext()) {
String s = it.next();
sum += s.length();
}
return sum;
}


Разумеется, в общем случае в list может оказаться всё что угодно и поэтому JIT-компилятору придётся сгенерировать честные виртуальные вызовы на месте iterator(), hasNext() и next(), что, конечно, не очень быстро. Но что случится, если мы всегда будем вызывать этот метод, подавая ему на вход singletonList? Давайте добавим простенький метод main():



public class Test {
static int res = 0;

public static void main(String[] args) {
for (int i = 0; i < 100000; i++) {
res += testIterator(Collections.singletonList("x"));
}
System.out.println(res);
}
}


Здесь мы вызываем наш testIterator в цикле достаточное количество раз, чтобы скомпилировать метод JIT-компилятором C2. Как некоторые из вас уже знают, в виртуальной машине HotSpot есть два JIT-компилятора: C1 (клиентский) и C2 (серверный). В 64-битной версии Java 8 с настройками по умолчанию они работают совместно. Сперва метод компилируется с помощью C1 (который компилирует быстро, но создаёт не очень оптимальный код). При этом в код добавляются дополнительные инструкции, которые собирают некоторую статистику (это называется "профилирование"). Это, конечно, замедляет выполнение, но пригождается в дальнейшем. Среди различных профилей собирается профиль типов. В нашем случае JVM внимательно следит, какой тип имеет параметр list при каждом вызове. И тут виртуальная машина замечает, что в 100% случаев на входе был список типа Collections$SingletonList (который возвращает метод singletonList).



Когда количество вызовов метода достигает некоторого порога, метод перекомпилируется компилятором C2, которому доступен собранный профиль. C2 делает разумное предположение, что раз до сих пор всегда был SingletonList, то и далее он будет часто попадаться. А значит, iterator() точно вызовет метод singletonIterator(). Но там уже нетривиальный объект, который, к примеру, содержит поле hasNext, чтобы отследить, что его не вызвали дважды, и кинуть если надо NoSuchElementException. Способен ли C2 с этим побороться?



Чтобы узнать ответ, мы можем попросить JIT-компилятор вывести ассемблер сгенерированный для методов. Для этого нам потребуется установить hsdis. Потом можно воспользоваться удобными инструментами вроде JITWatch или написать JMH-бенчмарк и воспользоваться опцией -perfasm. Но здесь у нас пример простой, поэтому мы обойдёмся без сторонних инструментов и просто запустим виртуальную машину с такими волшебными параметрами:



$ java -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintAssembly Test >output.txt


Будьте осторожны: вывод этой команды может напугать маленьких детей. Но порывшись в нём, вы найдёте код, сгенерированный для нашего метода testIterator. Вот что сгенерировал C2 на платформе Intel x64 с кучей до 4 Гб:



Ассемблер, можно не вчитываться
  # {method} {0x0000000055120518} 'testIterator' '(Ljava/util/List;)I' in 'Test'
# parm0: rdx:rdx = 'java/util/List'
# [sp+0x20] (sp of caller)
0x00000000028e7560: mov %eax,-0x6000(%rsp)
0x00000000028e7567: push %rbp
0x00000000028e7568: sub $0x10,%rsp ;*synchronization entry
; - Test::testIterator@-1 (line 15)

0x00000000028e756c: mov 0x8(%rdx),%r10d ; implicit exception: dispatches to 0x00000000028e75bd
0x00000000028e7570: cmp $0x14d66a20,%r10d ; {metadata('java/util/Collections$SingletonList')}
0x00000000028e7577: jne 0x00000000028e75a0 ;*synchronization entry
; - java.util.Collections::singletonIterator@-1
; - java.util.Collections$SingletonList::iterator@4
; - Test::testIterator@3 (line 16)

0x00000000028e7579: mov 0x10(%rdx),%ebp ;*getfield element
; - java.util.Collections$SingletonList::iterator@1
; - Test::testIterator@3 (line 16)

0x00000000028e757c: mov 0x8(%rbp),%r11d ; implicit exception: dispatches to 0x00000000028e75c9
0x00000000028e7580: cmp $0x14d216d0,%r11d ; {metadata('java/lang/String')}
0x00000000028e7587: jne 0x00000000028e75b1
0x00000000028e7589: mov %rbp,%r10 ;*checkcast
; - Test::testIterator@24 (line 16)

0x00000000028e758c: mov 0xc(%r10),%r10d ;*getfield value
; - java.lang.String::length@1
; - Test::testIterator@30 (line 17)

0x00000000028e7590: mov 0xc(%r10),%eax ;*synchronization entry
; - Test::testIterator@-1 (line 15)
; implicit exception: dispatches to 0x00000000028e75d5
0x00000000028e7594: add $0x10,%rsp
0x00000000028e7598: pop %rbp
0x00000000028e7599: test %eax,-0x27b759f(%rip) # 0x0000000000130000
; {poll_return}
0x00000000028e759f: retq
... // дальше холодные пути


Первое, что бросается в глаза — это краткость кода. С вашего позволения я прокомментирую, что тут происходит:



// Стандартный стековый фрейм - подобным образом начинается всякий JIT-компилированный метод
mov %eax,-0x6000(%rsp)
push %rbp
sub $0x10,%rsp
// Загружаем идентификатор класса объекта из переменной list (указатель на объект пришёл в метод в регистре rdx).
// Идентификатор класса лежит в объекте по смещению 0x8. Это похоже на вызов list.getClass().
// При этом здесь происходит неявная проверка на null. Если окажется, что передали в метод null,
// процессор сгенерирует аппаратное исключение в связи с обращением по запрещённому адресу.
// Исключение перехватит виртуальная машина и заботливо транслирует его в NullPointerException
mov 0x8(%rdx),%r10d
// Сравниваем list.getClass() с идентификатором класса Collections$SingletonList. Этот идентификатор не меняется
// за время работы JVM и, конечно, JIT его знает, поэтому это просто сравнение с константой
cmp $0x14d66a20,%r10d
// Если list - это не SingletonList, выпрыгиваем на холодный путь
jne 0x00000000028e75a0
// Читаем приватное поле Collections$SingletonList.element в регистр rbp. Хотя указатели 64-битные, при размере кучи
// меньше 4 Гб верхние 32 бита всегда нули, поэтому виртуальная машина их не хранит и копирует только 32 нижних бита в ebp
mov 0x10(%rdx),%ebp
// Читаем идентификатор класса элемента и сверяем его с идентификатором класса String (аналогично тому, что выше)
mov 0x8(%rbp),%r11d
cmp $0x14d216d0,%r11d
// Если элемент списка - не строка, выпрыгиваем наружу на холодный путь (там будет создано и выброшено ClassCastException)
jne 0x00000000028e75b1
// Читаем приватное поле String.value в регистр r10. Это массив char[], в котором хранится сама строка
mov %rbp,%r10
mov 0xc(%r10),%r10d
// Читаем длину массива в регистр eax, который стандартно используется для передачи возвращаемого значения метода
mov 0xc(%r10),%eax
// Восстановление стекового фрейма
add $0x10,%rsp
pop %rbp
// Проверка на safe-point. С её помощь JVM может забрать контроль у скомпилированного кода, например, для сборки мусора.
test %eax,-0x27b759f(%rip)
// Выход из метода
retq


Если кому-то всё ещё сложно это понять, давайте перепишем на псевдокоде:



if (list.class != Collections$SingletonList) {
goto SLOW_PATH;
}
str = ((Collections$SingletonList)list).element;
if (str.class != String) {
goto EXCEPTIONAL_PATH;
}
return ((String)str).value.length;


Видите? На горячем пути нет ни цикла, ни выделения памяти под итератор, ни одного вызова метода. Всего лишь несколько разыменований и две быстрые проверки (которые всегда были ложны, поэтому предсказание ветвлений в процессоре отработает на ура). JIT-компилятор заинлайнил всё, что можно, понял, что итератор из метода не убегает, избавился от выделения памяти, развернул цикл и даже смог удалить флаг hasNext и связанные с ним проверки, статически доказав, что они не нужны! Сложение и переменная sum также испарились. И тем не менее, метод полностью корректен. Если окажется, что при следующем вызове ему передадут не singletonList, а что-то другое, он просто уйдёт на холодный путь (который, конечно, значительно медленнее). Остальные исключительные ситуации тоже обрабатываются. Можно передать null вместо list или подсунуть в список не строку (слава type erasure) — всё это будет обработано в соответствии с семантикой языка.



Что же произойдёт, если сценарий работы программы изменится? Предположим, через некоторое время мы вообще перестали передавать в этот метод singletonList и передаём теперь всякие другие списки. На медленном пути виртуальная машина продолжает собирать статистику. Если она обнаружит, что медленный путь происходит сильно часто, JIT-компилятор перекомпилирует метод, убрав специальную обработку singletonList и вставив сразу честные виртуальные вызовы. Может ещё, кстати, сделать две ветки, если вы используете только две разные реализации списков. Этим JIT отличается от приложений скомпилированных заранее: исполняемый машинный код вашей программы может меняться, следуя за изменениями в её поведении.


Original source: habrahabr.ru.

https://habrahabr.ru/post/305894/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Из песочницы] Шлюз для почтового сервера

Пятница, 15 Июля 2016 г. 15:13 (ссылка)

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



У компании имеются удаленные партнерские филиалы, равномерно распределенные по всей Западной Европе, использующие один почтовый сервер. К сожалению, бюджет IT-отдела не очень большой, а пользователей достаточно много (около 700 почтовых аккаунтов). Использовался, да и сейчас используется для почты Exchange 2010 с последними обновлениями и более-менее настроенными правилами фильтрации спама, а вот с антивирусом как-то не заладилось. Купленное решение отказывалось нормально работать, раздувая очередь входящей корреспонденции до неприличных размеров и вешая намертво всю почту. (Да, я знаю, что все best practice говорят о необходимости и edge-сервера, и отдельно сервера архивации, но что было на тот момент, то и было.)



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



Из того, что было в конечном итоге протестировано и опробовано:



Zentyal, ASSP и Xeams.



Подробно описывать их не буду, но пару слов стоит сказать
Начнем по порядку. Zentyal отпал достаточно быстро, потому что цели менять всю инфраструктуру Active Directory и переезжать на open-source решение не стояло, да и вообще, он скорее представляет из себя эдакий комбайн из всего, что только возможно. Как мне кажется, он прекрасно подошел бы SMB до 50 пользователей.



Перейдем ко второму нашему кандидату. Anti-Spam SMTP Proxy. Весь набор для проксирования почтового трафика в нем есть, т.е. и Байесовская фильтрация спама, и пенальти очки, и черные/белые списки, и ClamAV и куча-куча всего еще. Есть единственный недостаток: юзабельность. Т.е. веб-интерфейс ASSP это обернутые в какой-то ад конфиги, которые лично мне проще править через консоль. Штука эта простояла у нас около 2х месяцев в продакшене, а потом какой-то из админов что-то поменял в конфигах и все заверте… В общем, мы поймали пару раза .locky и в последний раз актуального бэкапа не нашлось, потому человек ушел в отпуск, а когда пришел уже было поздно. Это заставило нас пересмотреть нашу политику резервного копирования, но об этом в другой раз. К огромному сожалению, отдельного Linux-администратора у нас нет, т.к. почти вся инфраструктура на Microsoft, поэтому ковырять монструозные награмождения через веб-интерфейс для админов была реальная пытка.





После усиленного гугления и пробы еще нескольких продуктов, я остановил свой выбор на Xeams. Да, к большому сожалению, это не OpenSource, а закрытый продукт, однако, он очень дружелюбен к тем администраторам, у которых нет большого опыта в связках Linux&Dovecot&Postfix&etc. Кроме того, он кросплатформенный, так что не возникнет проблем даже у тех, кто с линкусом на вы.



Работать он может в трех режимах:

Stand alone server — да, xeams умееть быть и просто почтовым сервером. На сколько хорошим не могу сказать, потому что эта функция интересовала меня в последнюю очередь.

Spam firewall — в этом режими xeams только принимает весь почтовый трафик на себя и дальше распределяет на корпоративные почтовые сервера.

Hybrid mode — Гибридный режим. Объединяет два других, рекомендую ставить его, поскольку полноценно фильтровать спам он не сможет без пропуска исходящего трафика через себя.



Так как у нас уже был почтовый сервер, то использовался вариант SMTP-Proxy

Так как у нас уже был почтовый сервер, то использовался вариант SMTP-Proxy



Установка производилась на чистую машину с Ubuntu 14.04. Единственное, что необходимо было доставить, так это Java, на которой, собственно, работает движок Xeams.



sudo apt-get update && upgrade
sudo apt-get install libc6-i386
sudo update-alternatives --config java
sudo apt-get install default-jre
java -version


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



sudo apt-get install clamav-daemon




Качаем архив с инсталлятором отсюда. Распаковываем, даем права и выполняем.

wget http://www.xeams.com/files/XeamsLinux64.tar
tar -xf ./XeamsLinux64.tar
chmod +x ./Install.sh
./Install.sh




Если все прошло успешно, Web-Interface будет доступен по 5272 порту.



Главная страница содержит отчеты, графики и основные параметры потребления ресурсов. Мы дали машине 8Гб RAM, пиковая нагрузка несколько раз достигала 6, так что такие цифры вполне оправданы.







Переходим к самой настройке. На Firewall весь трафик на 25 порт заворачиваем на IP нашего Xeams на порт 2525. Это сделано потому, что у нас Xeams не только получает, но через него и отправляется почта, так что входящая приходит на 2525й порт, а исходящая на 25й порт.



В целом, настройка Xeams простая и достаточно удобная. На которые моменты все-таки, хотелось бы обратить внимание:



Server Configuration > Server Configuration > Basic




Указываем порт для http/https web-доступа.

DNS, если указан в настройках самой машины, то можно не указывать

И адрес, для ежедневных отчетов.





Server Configuration > Server Configuration > Advanced




На вкладке Advanced необходимо указать HELO (как правило, можно просто скопировать из настроек имеющегося почтового сервера), чтобы самим не попасть в СпамЛисты.





Server Configuration > SMTP Configuration > Relaying




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

Со стороны Exchange это выглядит вот так, где закрашенным прописан адрес smtp-relay









Server Configuration > SMTP Proxy Server Configuration




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





Greylisting
Отдельно хотелось бы сказать о


href=«ru.wikipedia.org/wiki/%D0%A1%D0%B5%D1%80%D1%8B%D0%B9_%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA»>Грейлистинге
. Настоятельно рекомендую прописать туда все домены публичной почты и белого списка, потому что задержка письма на 10 часов из-за неправильно настройки на стороне отправляющего почтового сервера, увы, не редкость.





Server Configuration > Active Directory Integration




Если у Вас поднят Active Directory, то его без проблем можно интегрировать в Xeams. Зачем? Хотя бы для того, чтобы пользователи не мучали Вас в первое время, что у них не доходит почта. Пользователь без проблем сможет зайти и проверить свой почтовый ящик, просмотреть свой спам и прописать свои black/whitelist:







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





ClamAV
Если Вы, также, как и я установили ClamAV на тот же сервер, что и Xeams, то страница настройки будет приглядеть примерно вот так:







чуть ниже я расскажу о некоторых нюансах его использования.





Спамфильтры.



У Xeams достаточно большой набор спамфильтров:



Real-time Black-hole servers (RBL). Каждому списку можно выставить свои очки значимости, на основе которых, Xeams определяет благонадежность письма.



Из адаптивных фильтров используется Bayesian Analysis, которая вполне справляется с возложенными на нее функциями. Были опасения, что работать он будет плохо, из-за большого количества языков переписки (русский, немецкий, английский, испанский, итальянский, польский, греческий), однако, процент ложных срабатываний достаточно низок.



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



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



Теперь несколько слов хотелось бы уделить не совсем очевидным нюансам:

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



Градация писем по очкам


и Bayesian Score подняли до 115.

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



По умолчанию, ClamAV НЕ проверяет вложения макросов в документах Ofiice, т.е. новомодный .locky может спокойно пройти,

исправить это можно одной строчкой:



echo 'ScanOLE2 true' >> /etc/clamav/clamd.conf


Если кому интересно, то вот конфиг, который используется у нас сейчас
cat /etc/clamav/clamd.conf                  

#Automatically Generated by clamav-base postinst
#To reconfigure clamd run #dpkg-reconfigure clamav-base
#Please read /usr/share/doc/clamav-base/README.Debian.gz for details
TCPSocket 3310
# TemporaryDirectory is not set to its default /tmp here to make overriding
# the default with environment variables TMPDIR/TMP/TEMP possible
User clamav
AllowSupplementaryGroups true
ScanMail true
ScanArchive true
ArchiveBlockEncrypted false
MaxDirectoryRecursion 15
FollowDirectorySymlinks false
FollowFileSymlinks false
ReadTimeout 180
MaxThreads 12
MaxConnectionQueueLength 15
LogSyslog true
LogRotate true
LogFacility LOG_LOCAL6
LogClean false
LogVerbose false
PidFile /var/run/clamav/clamd.pid
DatabaseDirectory /var/lib/clamav
SelfCheck 3600
Foreground false
Debug false
ScanPE true
MaxEmbeddedPE 10M
ScanOLE2 true
ScanPDF true
ScanHTML true
MaxHTMLNormalize 10M
MaxHTMLNoTags 2M
MaxScriptNormalize 5M
MaxZipTypeRcg 1M
ScanSWF true
DetectBrokenExecutables false
ExitOnOOM false
LeaveTemporaryFiles false
AlgorithmicDetection true
ScanELF true
IdleTimeout 30
PhishingSignatures true
PhishingScanURLs true
PhishingAlwaysBlockSSLMismatch false
PhishingAlwaysBlockCloak false
PartitionIntersection false
DetectPUA false
ScanPartialMessages false
HeuristicScanPrecedence false
StructuredDataDetection false
CommandReadTimeout 5
SendBufTimeout 200
MaxQueue 100
ExtendedDetectionInfo true
OLE2BlockMacros false
ScanOnAccess false
AllowAllMatchScan true
ForceToDisk false
DisableCertCheck false
DisableCache false
MaxScanSize 100M
MaxFileSize 25M
MaxRecursion 10
MaxFiles 10000
MaxPartitions 50
MaxIconsPE 100
StatsEnabled false
StatsPEDisabled true
StatsHostID auto
StatsTimeout 10
StreamMaxLength 25M
LogFile /var/log/clamav/clamav.log
LogTime true
LogFileUnlock false
LogFileMaxSize 0
Bytecode true
BytecodeSecurity TrustSigned
BytecodeTimeout 60000
OfficialDatabaseOnly false
CrossFilesystems true
OnAccessMaxFileSize 25M






В целом, через две недели после использования и анализа спама, возможного спама, можно сказать, что 98% спама действительно не проходит.



Буду рад предложениям, комментариям и критики в комментах.
Original source: habrahabr.ru.

https://habrahabr.ru/post/305746/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Как ускорить сборку с Maven

Пятница, 15 Июля 2016 г. 08:22 (ссылка)

Maven

Что делать, если сборка (build) с Maven проходит слишком медленно? Ведь когда сборка длится слишком долго, любой, даже самый терпеливый разработчик, может заскучать и отвлечься.



Для быстрого поиска в Google или для закладок, сразу предлагаю итоговое решение:

mvn package -am -o -Dmaven.test.skip -T 1C


— для сборки проекта без тестов.



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

mvn clean package


, ну а многие командой:

mvn clean install


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



package вместо install



Согласно жизненному циклу сборки проекта после фазы package, на которой мы получаем полноценный jar-файл, есть фаза verify, а затем install. Часто необходимо получить именно jar-файл, который нет необходимости помещать в локальный репозиторий, поэтому в данной ситуации я остановился на package и сэкономил немного времени.



Параллельная сборка



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

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

mvn -T 4 package


или

mvn -T 1C package


В первом случае вы указываете число потоков для использования Maven, а во втором — что необходимо использовать один поток на каждое ядро CPU.

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

Поэкспериментировав с количеством потоков на ядро, попробовав следующие варианты:

mvn -T 1C package
mvn -T 1.5C package
mvn -T 2C package


— я не получил какого-то заметного прироста в случае с более чем 1 потоком на ядро на собираемом проекте на процессоре Core i7.

Замечу ещё, что параллельная сборка — это экспериментальная функция Maven 3. При сборке могут возникнуть проблемы при использовании плагинов, не являющихся @threadSafe. В частности, это плагины:


  • Surefire с параметром forkMode=never, surefire [2.6,) предупреждает (assert) об этом.

  • maven-modello-plugin, исправлено с версии 1.4,

  • Все клиенты maven-archiver (EAR, EJB, JAR, WAR etc), исправлено в последних версиях



и библиотеки:


  • plexus-utils 2.0.5

  • maven-archiver 2.4.1

  • plexus-archiver 1.0

  • plexus-io 1.0





Инкрементальная сборка



Как мы обсуждали выше, проект чаще всего собирается командой:

mvn clean package


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

mvn package -am


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

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



Оффлайновая сборка



Часто бывает так, что артефакты на внешних репозиториях обновляются не так уж и часто, особенно, когда это артефакты определённой версии, или эти артефакты должны поддерживаться вашими усилиями (- Добро пожаловать в новый проект!). Довольно логично в такой ситуации сообщить Maven, что не нужно каждый раз заново скачивать их из репозиториев:

mvn package -o


или

mvn package --offline




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



Пропуск тестов



Пожалуй, самое спорное предложение по оптимизации скорости сборки я оставил напоследок. Тем, кто полностью разделяет ценности TDD, я просто предлагаю пролистнуть этот абзац и убрать из нашей итоговой команды параметр -Dmaven.test.skip.

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

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

mvn package -DskipTests


Но при этом можно также пропустить компиляцию тестов, если выполнить команду в следующем виде:

mvn package -Dmaven.test.skip




Подведём итоги



Итак, ещё раз команда, которая получилась у нас для ускорения сборки с Maven:

mvn package -am -o -Dmaven.test.skip -T 1C




Результат, показанный на собираемом мной проекте, радует: сборка вместо 25 минут стала проходить за 30 секунд.

Авторы некоторых других статей по оптимизации скорости сборки с Maven также рекомендуют использовать параметры оптимизации запуска компилятора:

MAVEN_OPTS= -XX:+TieredCompilation -XX:TieredStopAtLevel=1
mvn package


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



Надеюсь, что в комментариях вы также поделитесь статистикой по ускорению сборки ваших проектов!
Original source: habrahabr.ru.

https://habrahabr.ru/post/304164/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Как написать SQL-запрос на Slick и не открыть портал в ад

Четверг, 14 Июля 2016 г. 19:01 (ссылка)





Slick — это не только фамилия одной из величайших солисток всех времён, но и название популярного Scala-фреймворка для работы с базами данных. Этот фреймворк исповедует «функционально-реляционный маппинг», реализует реактивные паттерны и обладает официальной поддержкой Lightbend. Однако отзывы разработчиков о нём, прямо скажем, смешанные — многие считают его неоправданно сложным, и это отчасти обоснованно. В этой статье я поделюсь своими впечатлениями о том, на что стоит обратить внимание при его использовании начинающему Scala-разработчику, чтобы в процессе написания запросов случайно не открыть портал в ад.



Фреймворк Slick, как это часто случается в мире Scala, сравнительно недавно пережил существенный редизайн — версия 3 была заточена под реактивность и сильно поменяла API, сделав его ещё более функциональным, чем прежде — и теперь большое количество статей и ответов на StackOverflow, рассчитанных на версию 2, стало неактуальным. Документация на фреймворк достаточно лаконичная и представляет собой скорее список примеров; концептуальные вещи (в частности, активное использование монад) в ней объясняются достаточно поверхностно. Предполагается, что многие аспекты функционального программирования на Scala и продвинутые фичи языка разработчику уже хорошо известны.



Результатом стали подобные вопросы на StackOverflow, за которые мне теперь немного стыдно: там я бился над некомпилирующимся кодом, потому что не понимал архитектуры фреймворка и тех монадических паттернов, которые в нём заложены. Об этих паттернах и их применении в Slick мне и хотелось бы рассказать в этой статье: возможно, кому-то они сберегут многие часы мучений в попытках написать что-то более сложное, чем простейший запрос.



Монады и построитель запросов



Одним из важных компонентов любой типобезопасной библиотеки для работы с базами данных является построитель запросов, который позволяет из типизированного кода на языке программирования сформировать нетипизированную строку на языке SQL. Вот пример построения запроса с использованием Slick, взятый из документации, из раздела про «монадические джойны»:



val monadicInnerJoin = for {
c <- coffees
s <- suppliers if c.supID === s.id
} yield (c.name, s.name)
// compiles to SQL:
// select x2."COF_NAME", x3."SUP_NAME"
// from "COFFEES" x2, "SUPPLIERS" x3
// where x2."SUP_ID" = x3."SUP_ID"


Признаюсь, для новичка в Scala это выглядело довольно странно. Если долго медитировать на этот код, то можно заметить соответствия между этой хитрой синтаксической конструкцией и приведённым ниже SQL-запросом, в который она трансформируется. Вроде что-то становится понятно: справа от стрелочек таблицы, слева — алиасы, после if — условие, в yield — поля, выбранные для проекции. Выглядит как SQL-запрос, вывернутый наизнанку. Но почему построитель реализован именно так? При чём тут вообще for? Разве здесь есть какая-то итерация по содержимому таблиц? Ведь в этот момент мы ещё не исполняем запрос, а только строим его.



Без понимания того, как эта конструкция работает, конечно, можно привыкнуть к такому синтаксису и клепать подобные запросы по аналогии. Но при попытке написать что-то более сложное мы рискуем натолкнуться на стену непонимания и потратить кучу времени, проклиная компилятор на чём свет стоит, как это было и со мной в своё время. Чтобы понять, что скрыто за этой магией, и почему построитель запросов реализован именно так, придётся сделать небольшое лирическое отступление про for-включения и монады.



Монады



Что характерно, в книге Мартина Одерски «Programming in Scala» слово «монада» употребляется в одном-единственном месте — как раз в самом конце главы про for-включение, как бы между делом. Большая часть этой главы — описание того, как можно пользоваться синтаксической конструкцией for для итерации по коллекции, нескольким коллекциям, для фильтрации. И лишь в самом конце говорится о том, что есть такая штука как «монада», с которой тоже удобно работать с помощью for-включения, но подробного объяснения того, что это и зачем, не даётся. Между тем, использование for-включения для оперирования монадами является весьма эффектным и одновременно непонятным синтаксическим конструктом для взгляда новичка.



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




  • операция return — заворачивает (или «поднимает», «lifts») значение в некоторый контекст, представляемый этим типом;

  • операция bind — выполняет некоторую трансформирующую функцию над значением в этом контексте.



С точки зрения авторов языка Scala, в ООП операция return по сути реализуется конструктором экземпляра, принимающим значение (конструктор как раз позволяет «завернуть» переданное значение в объект), а операции bind соответствует метод flatMap. На самом деле монады в Scala — это не совсем монады в понимании классических функциональных языков типа Haskell, а, скорее, «монады по-одерски». И хотя в классических книгах по Scala избегают термина «монада», и даже в стандартной библиотеке вы с трудом найдёте упоминание этого слова, разработчики Slick не стесняются использовать его в документации и коде, полагая, что читателю уже известно, что это такое.



for-включения



На самом деле for-comprehension— это, конечно, не цикл, и ключевое слово for может поначалу сбить с толку. Кстати, я пытался разобраться, как же переводится на русский язык «for-comprehension» — варианты есть, а общепринятого нет. Некоторую полемику на эту тему можно почитать тут, тут и тут.



Я остановился на термине «for-включение», потому что оно обычно описывает включение элементов в выходное множество по определённым правилам. Хотя, если рассматривать for-comprehension как monadic comprehension, то такой перевод становится не столь очевиден. Ввиду небольшого количества литературы по ФП и теории категорий на русском языке, термин на текущий момент не устоялся.



Ирония в том, что, по мнению авторов Programming in Scala, одна из наилучших областей применения for-включения — это комбинаторные головоломки:







Всё это замечательно и полезно, но как насчёт реальных кейсов применения?



Оказывается, мощь паттерна монады, особенно в сочетании с for-включением, заключается в том, что он позволяет выполнять высокоуровневую композицию отдельных действий в достаточно сложном контексте, иначе говоря, строить из маленьких кубиков (операций bind/flatMap) более сложные конструкции. Синтаксис for-включения даёт возможность выстраивать в последовательную цепочку такие действия, которые на самом деле нельзя выполнить последовательно. Обычно сложность их выполнения заключается в наличии какого-то сложного контекста. Например, одна из часто используемых монад в Scala — это List:



  // списки
val people = List("Воронин", "Гейгер", "Убуката")
val positions = List("мусорщик", "следователь", "редактор")

// декартово произведение списков с использованием for-включения:
val peoplePositions = for {
person <- people
position <- positions
} yield s"$person, $position"


С помощью for-включения над отдельными экземплярами монады List можно выполнять декартово произведение, т.е. композицию списков. Монада при этом скрывает от нас сложность контекста (итерацию по множеству значений).



На деле же for-включение — это просто синтаксический сахар с строго определёнными правилами преобразования. В частности, все стрелочки, кроме последней, превращаются в вызовы flatMap у идентификаторов справа, а последняя стрелочка — в вызов map. Идентификаторы слева при этом трансформируются в аргументы функций для методов flatMap, а содержимое yield — это то, что возвращается из последней функции.



Поэтому можно записать то же самое и с использованием прямого вызова методов flatMap и map, но выглядит это несколько менее наглядно, особенно если размеры и вложенность этих конструкций будут в несколько раз больше:



  // декартово произведение списков прямым вызовом flatMap и map:
val peoplePositions2 = people.flatMap {person =>
positions.map { position =>
s"$person, $position"
}
}


Аналогично, монадическая реализация Future позволяет выстраивать действия над значениями в цепочки, скрывая от нас сложность контекста (асинхронность выполнения действий и тот факт, что вычисление значений отложено):



  // первая футура формирует и возвращает строку
def getFuture1 = Future {
"1337"
}

// вторая футура из строки делает число
def getFuture2(string: String) = Future {
string.toInt
}

// комбинированная футура, созданная с использованием for-включения
val composedFuture = for {
result1 <- getFuture1
result2 <- getFuture2(result1)
} yield result2


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



  // комбинированная футура, созданная с использованием flatMap и map
val composedFuture2 = getFuture1.flatMap { string =>
getFuture2(string).map { int =>
int
}
}


for-включение, монады и построение запросов



Итак, операция flatMap является средством композиции монадических объектов, или построения сложных структур из простых кирпичиков. Что же касается языка SQL, то там тоже есть средство для композиции — это предложение JOIN. Если теперь вернуться к for-включению и его использованию для построения запросов, то становится очевидным, что flatMap и JOIN имеют много общего, и отображение одного на другое вполне осмысленно и разумно. Посмотрим ещё раз на пример построения запроса с внутренним джойном, который приводился в начале статьи. Теперь идея, заложенная в такой синтаксис, должна стать несколько понятнее:



val monadicInnerJoin = for {
c <- coffees
s <- suppliers if c.supID === s.id
} yield (c.name, s.name)


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



Мало того, в Slick монады используются аж на двух уровнях — в конструкторе запросов (как отдельные компоненты запроса, которые можно объединять) и при композиции действий с базой данных (их можно объединять в комплексные действия, которые затем завернуть в транзакцию). Честно говоря, это поначалу доставляло мне немало проблем, потому что с помощью for-включения можно объединять как монадические запросы, так и монадические действия, и я долго «намётывал глаз», пока не научился в коде отличать одну монаду от другой. Монадические действия — это как раз тема следующей главы…



Монады и композиция действий с базой данных



Довольно теории, приступим к хардкору. Попробуем написать на Slick что-нибудь более полезное, чем простой запрос. Начнём опять-таки с запроса с внутренним джойном:



  val monadicInnerJoin = for {
ph <- phones
pe <- persons if ph.personId === pe.id
} yield (pe.name, ph.number)


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



// делаем из запроса DBIO-действие
val action1 = monadicInnerJoin.result


Любое действие, в том числе и композитное, можно выполнить в рамках транзакции:



val transactionalAction1 = action1.transactionally


Но как быть, если нам нужно завернуть в транзакцию несколько отдельных действий, некоторые из которых вообще не связаны с базой данных? В этом нам поможет метод DBIO.successful:



// делаем DBIO-действие из какой-то произвольной функции
val action2 = DBIO.successful {
println("Делаем что-то между запросами в транзакции...")
}


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



// ещё парочка DBIO-действий...
val action3 = persons += (1, "Grace")
val action4 = phones += (1, 1, "+1 (800) FUC-KYOU")

// делаем композитное действие из всех четырёх действий
val compositeAction = for {
result <- action1
_ <- action2
personCount <- action3
phoneCount <- action4
} yield personCount + phoneCount


Обратите внимание — если результат действия нас не интересует (оно выполняется ради побочного эффекта), то слева от стрелочки можно поставить символ подчёркивания. Теперь завернём композитное действие в транзакцию и создадим на основе него футуру:



// заворачиваем композитное действие в транзакцию и делаем из него футуру
val actionFuture = db.run(compositeAction.transactionally)


Ну и наконец скомпонуем эту футуру с другой футурой с помощью всемогущего for и дождёмся её выполнения с помощью Await.result:



val databaseFuture = for {
i <- actionFuture
_ <- Future {
println(s"Вставлено записей: $i")
}
} yield ()

Await.result(databaseFuture, 1 second)


Вот так всё просто.



Заключение



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



Надеюсь, эта статья поможет новичкам в Scala и Slick не отчаяться и обуздать всю мощь этого фреймворка. Исходный код к статье доступен на GitHub.



Original source: habrahabr.ru.

https://habrahabr.ru/post/305682/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Из песочницы] Одна из задач при собеседовании и попытка найти правильное решение

Среда, 13 Июля 2016 г. 16:13 (ссылка)

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



Суть задачи такова. Необходимо написать метод, который принимает в качестве аргументов две коллекции ArrayList одинакового размера, например, одна a = [a1, a2, a3, ....], а вторая b = [b1, b2, b3 ...]. Метод должен превратить коллекцию а в следующий вид: a = [a1, b1, a2, b2, a3, b3....]. При этом решение должно быть экономным к ресурсам и процессорному времени.



Первый вариант, который может быть придуман фактически в лоб и этот вариант я тоже видел на форуме:



    static void merge4(ArrayList a, ArrayList b) {
int size = a.size();
int count = 0;
for (int i = 0; icode>


Когда тестировал метод, который принимает две коллекции с количеством элементов 1 000 000, то еле дождался результата. Получил 76 867 мс, печально, хотя вполне ожидаемо, когда каждая вставка методом add, сопровождается копированием массива, что находится правее… При миллионе элементов это, увы, очень плохой вариант.



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



	static void merge3(ArrayList a, ArrayList b) {
ArrayList c = new ArrayList(a.size()*2);
for (int i=0; icode>


Это заняло около 6 мс. Однако вполне очевидно, что данный способ не вполне экономен в части Memory.



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



	static void merge2(ArrayList a, ArrayList b) {
int size = b.size();
for (int i = 0; i < size; i++) {
a.add(null);
}
for (int i = b.size(); i > 0; i--) {
a.set((i << 1) - 1, b.get(i - 1));
a.set((i << 1) - 2, a.get(i - 1));
}
}


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



Последний вариант, я думал оптимизировал за счет того, что использую метод a.ensureCapacity(size * 2) с мыслями, что как минимум я избавлюсь от одного лишнего копирования массива, потому что при превышении текущего размера Array коллекция увеличивает число нового массива в 1,5 больше от текущего, а так как нам нужна в итоге коллекция с удвоенным числом элементов, то это потом приведет еще к одному увеличению размера массива, которое нам по факту не нужно (т.е. чуточку экономит ресурсы Memory), т.е. по факту выглядело бы это так:



	static void merge1(ArrayList a, ArrayList b) {

int size = b.size();
a.ensureCapacity(size * 2);
for (int i = 0; i < size; i++) {
a.add(null);
}

for (int i = b.size(); i > 0; i--) {
a.set((i << 1) - 1, b.get(i - 1));
a.set((i << 1) - 2, a.get(i - 1));
}

}


Выгода по времени не существенна, но вроде 1мс есть. Но плюс все-таки есть. Пусть в каждой из коллекций, передаваемых в метод было size элементов, то при вызове метода merge1 там будет size*2 элементов. Для метода merge1 в принципе при вызове a.size() покажет такой же результат, но в основе этой коллекции до вызова метода trimToSize() будет массив с количеством элементов 1,5*1,5*size, т.е. мы сэкономили уток ресурсов.



Вообщем, ближе к самому оптимальному варианту решения задачи с учетом условия задачи, которое как минимум я нашел:



	static void merge0(ArrayList a, ArrayList b) {
a.addAll(b);
for (int i = b.size(); i > 0; i--) {
a.set((i << 1) - 1, b.get(i - 1));
a.set((i << 1) - 2, a.get(i - 1));
}
}


Итог 5 мс, и, как мне кажется, перерасхода ресурсов тоже нету.



Задача оказалась достаточно интересной, для ее решения мне дали 30 минут.



Однако, сама постановка задачи вроде и имеет цель оценить знания внутренней реализации коллекции, но в то же время все-таки изменять один из передаваемых параметров в методе является дурным тоном, поэтому стоило бы ее переформулировать немного ее в другом контексте.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/305612/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество

Следующие 30  »

<java - Самое интересное в блогах

Страницы: [1] 2 3 ..
.. 10

LiveInternet.Ru Ссылки: на главную|почта|знакомства|одноклассники|фото|открытки|тесты|чат
О проекте: помощь|контакты|разместить рекламу|версия для pda