Даведнік па Java

Карысныя спасылкі

Хатняя старонка на Oracle.com: https://www.oracle.com/java/

Даведнік па Java на Oracle.com: http://docs.oracle.com/javase/tutorial/index.html

Шэраг даведнікаў, прысьвечаных розным тэмам вакол Java на tutorialspoint.com: http://www.tutorialspoint.com/java_technology_tutorials.htm

Java: The Complete Reference (дзевятае выданьне папяровай кнігі, аўтарства Herbert Schildt ад выдавецтва Oracle Press, на жаль электроннага варыянту няма ў вольным доступе). Ці перакладзенае на расейскую мову выданьне гэтай кнігі: ozon.ru.

baeldung.com

Ядро

Тыпы даных

Апэрацыі

Інструкцыі

Клясы

Generics

Выключэньні

Multithreading

I/O

Lambda-выразы

Lambda-выразы – гэта рэалізацыя абстрактнага мэтаду функцыянальнага інтэрфэйсу. Функцыянальны інтэрфэйс – гэта інтэрфэйс, які ўтрымлівае адзін і толькі адзін мэтад. Самымі відавочнымі прыкладамі функцыянальнага інтэрфэйсу зьяўляюцца старыя добрыя інтэрфэйсы кшталту:

  • java.lang.Runnable з адзіным мэтадам void run();
  • альбо java.awt.event.ActionListener з адзіным мэтадам void actionPerformed(ActionEvent e).

Любы інтэрфэйс з адзіным мэтадам зьяўляецца функцыянальным, нічога дадаткова рабіць ня трэба, але аўтар такога інтэрфэйсу можа яўна пазначыць свой намер і абмежаваць зьмены ў такім інтэрфэйсе праз анатацыю @FunctionalInterface. У такім выпадку кампілятар ня дасьць дадаць у такі інтэрфэйс дадатковы мэтад, альбо прыбраць існы. Вось як выглядае рэалізацыя інэтрфэйсу Runnable:


@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

Адпаведна, калі да 8-ай вэрсіі неабходна было пісаць:


new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello from thread");
    }
}).start();

Пачынаючы з 8-ай вэрсіі, тое ж самае можна зрабіць наступным чынам:


new Thread(() -> System.out.println("Hello from thread")).start();

Як бачым варыянт з лямбда-выразам значна больш ляканічны.

Разгледзім сынтаксіс лямбда-выразу. Спачатку ідзе сьпіс парамэтраў у дужках. Тыпы парамэтраў пазначаць не абавязкова, яны будуць аўтаматычна вызначаныя кампілятарам у залежнасьці ад кантэксту, дзе лямбда-выраз выкарыстоўваецца. Калі парамэтар адзіны, дужкі не абавязковыя:


new JButton().addActionListener(e -> System.out.println(e.getActionCommand()));

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


interface NumericFunc {
    int func(int n);
}
class BlockLambdaDemo {
    public static void main(final String args[]) {
        final NumericFunc factorial = (n) -> {
            int result = 1;
            for(int i=1; i <= n; i++) result = i * result;
            return result;
        };
        System.out.println("The factoral of 3 is " + factorial.func(3));
        System.out.println("The factoral of 5 is " + factorial.func(5));
    }
}

Generic функцыянальныя інтэрфэйсы

Лямбда-выразы ня могуць вызначаць тыпы сваіх аргумэнтаў, яны вызначаюцца аўтаматычна ў залежнасьці ад інтэрфэйсаў, якія лямбда-выразы рэалізуюць. Таму лямбда-выразы ня могуць быць generic. Але самі функцыянальныя інтэрфэйсы могуць быць такімі:


// A generic functional interface.
interface SomeFunc {
    T func(T t);
}

class GenericFunctionalInterfaceDemo {
    public static void main(String args[]) {
        // Use a String-based version of SomeFunc.
        SomeFunc reverse = (str) -> {
            String result = "";
            for(int i = str.length()-1; i >= 0; i--) result += str.charAt(i);
            return result;
        };
        System.out.println("Lambda reversed is " + reverse.func("Lambda"));
        System.out.println("Expression reversed is " + reverse.func("Expression"));
        
        // Now, use an Integer-based version of SomeFunc.
        SomeFunc factorial = (n) -> {
            int result = 1;
            for(int i=1; i <= n; i++) result = i * result;
            return result;
        };
        System.out.println("The factoral of 3 is " + factorial.func(3));
        System.out.println("The factoral of 5 is " + factorial.func(5));
    }
}

Прадвызначаныя інтэрфэйсы агульнага прызначэньня

Пакет java.util.function у Java 8 утрымлівае шэраг прадвызначаных функцыянальных інтэрфэйсаў агульнага прызначэньня (унівэрсальных):

Інтэрфэйс Прызначэньне
UnaryOperator<T> Ужывае ўнарную апэрацыю да аб'екта тыпу T і вяртае вынік таго ж тыпу. Мэтадам гэтага інтэрфэйсу зьяўляецца мэтад apply(). Рэалізуецца як Function<T, T>
BinaryOperator<T> Ужывае бінарную апэрацыю да 2 аб'ектаў тыпу T і вяртае вынік таго ж тыпу. Мэтад apply(). Рэалізуецца як BiFunction<T, T, T>
DoubleBinaryOperator Ужывае бінарную апэрацыю да 2 аб'ектаў тыпу double і вяртае вынік таго ж тыпу. Мэтад applyAsDouble().
Consumer<T> Ужывае апэрацыю да аб'екта тыпу T. Мэтад accept(). Усе consumer-апэрацыі ў адрозьненьні ад іншых маюць пабочны эфэкт.
BiConsumer<T, U> Ужывае апэрацыю да двух сваіх аргумэнтаў, адзін тыпу T, іншы тыпу U. Мэтад accept()
Supplier<T> Вяртае аб'ект тыпу T. Мэтад get()
BooleanSupplier Вяртае значэньне тыпу boolean. Мэтад getAsBoolean()
Function<T, R> Ужывае апэрацыю да аб'екта тыпу T і вяртае аб'ект тыпу R у якасьці выніку. Мэтад apply()
BiFunction<T, U, R> Ужывае апэрацыю да аб'ектаў тыпу T і тыпу U, потым вяртае аб'ект тыпу R у якасьці выніку. Мэтад apply()
Predicate<T> Вызначае ці адпавядае аб'ект тыпу T пэўным умовам, аб чым кажа значэньне тыпу boolean, якое вяртае ягоны мэтад test()
BiPredicate<T> Вызначае ці адпавядаюць аб'екты тыпу T і тыпу U пэўным умовам, аб чым кажа значэньне тыпу boolean, якое вяртае ягоны мэтад test()
Дарабіць, ня ўсе інтэрфэйсы пералічаны

Большасьць з гэтых інтэрфэйсаў рэалізуюць змоўчны мэтад andThen() для магчымасьці спалучаць пасьлядоўныя выклікі ў ланцуг.

Спасылкі на мэтады

Спасылкі на мэтады дазваляюць, аднойчы вызначыўшы пэўны мэтад, перадаваць яго як лямбда-выраз. Уявім у нас ёсьць наступны код, напісаны пры дапамозе звычайнага лямбда-выразу (рэалізацыя мэтаду boolean accept(File pathname) функцыянальнага інтэрфэйсу java.io.FileFilter):


File[] hiddenFiles = mainDirectory.listFiles(f -> f.isHidden());

Тое ж самае можна напісаць яшчэ больш ляканічна і выразна, а галоўнае без неабходнасьці кожны раз вызначаць адзін і той жа лямбда-выраз у шматлікіх месцах, пры дапамозе спасылкі на мэтад:


File[] hiddenFiles = file.listFiles(File::isHidden);

Існуе 4 асноўных тыпаў спасылак на мэтады:

  1. Спасылка на статычны мэтад:
    
    // Вызначэньне Function<String, Integer> кажа аб тым, што гэта функцыя, 
    // якая прымае String у якасьці адзінага парамэтру і вяртае Integer.
    // apply - мэтад які запускае функцыю на выкананьне.
    Function<String, Integer> converter = Integer::parseInt;
    Integer number = converter.apply("10");
    
  2. Спасылка на мэтад экзэмпляру клясы:
    
    Function<Invoice, Integer> invoiceToId = Invoice::getId;
    
  3. Спасылка на мэтад пэўнага экзэмпляру клясы:
    
    Consumer<Object> print = System.out::println;
    
    Асабліва карысны гэты тып для выпадкаў, калі вы жадаеце зрабіць іньекцыю прыватнага дапаможнага мэтаду ў іншае месца праграмы:
    
    ...
    File[] hidden = mainDirectory.listFiles(this::isXML);
    ...
    
    private boolean isXML(File f) {
        return f.getName.endsWith(".xml");
    }
    
  4. Спасылка на канструктар:
    
    Supplier<List<String>> listOfString = List::new;
    

Выніковы прыклад

Напрыканцы разгледзім паступовую пераробку аднаго прыкладу з цалкам Java 7 варыянту ў цалкам Java 8 варыянт. Уявім, што ў нас ёсьць наступны код упарадкаваньня сьпісу інвойсаў па іх велічыні:


Collections.sort(invoices, new Comparator() {
    public int compare(Invoice inv1, Invoice inv2) {
        return Double.compare(inv2.getAmount(), inv1.getAmount());
    }
});

Па-першае Comparator зьяўляецца функцыянальным інтэрфэйсам, адзіны мэтад якога прымае 2 аргумэнты аднаго тыпу і вяртае цэлалікавае значэньне, і ідэальна падыходзіць для замены ананімнай яго рэалізацыі лямбда-выразам:


Collections.sort(invoices, 
    (Invoice inv1, Invoice inv2) -> return Double.compare(inv2.getAmount(), inv1.getAmount()));

Далей, у Java 8 мэтад упарадкаваньня быў уведзены ў сам сьпіс, таму няма неабходнасьці ўжываць дапаможны мэтад Collections.sort:


invoices.sort((Invoice inv1, Invoice inv2) -> return Double.compare(inv2.getAmount(), inv1.getAmount()));

Наступны крок. У Java 8 маецца дапаможны мэтад Comparator.comparing, які ў якасьці аргумэнту прымае лямбда-выраз для атрыманьня ключа, па якому будзе рабіцца ўпарадкаваньне, і сам створыць адпаведны аб'ект Comparator:


invoices.sort(Comparator.comparing(inv -> inv.getAmount()));

Наступным крокам можна лямбда-выраз у апошнім прыкладзе замяніць на спасылку на мэтад:


invoices.sort(Comparator.comparing(Invoice::getAmount));

Як бачым, апошні варыянт нашмат больш ляканічны і выразны.

Бібліятэка

Апрацоўка сымбальных чародаў

Матэматычныя вылічэньні

Рознае ў java.lang

Значэньні даты і часу

Date and Time API у Java 8 падвергнулася значным зьменам. Па-першае, аб'екты даты і часу сталі immutable, што вельмі важна для пазьбяганьня памылак. Па-другое, API стаў значна больш domain driven і інтуітыўна зразумелым:


LocatedDateTime coffeeBreak = LocalDateTime.now().plusHours(2).plusMinutes(30);

Разгледзім комплексны прыклад для ілюстрацыі магчымасьцяў:


ZoneId london = ZoneId.of("Europe/London");
LocalDate may1 = LocalDate.of(2016, Month.MAY, 1);
LocalTime early = LocalTime.parse("08:45");
ZonedDateTime departure = ZonedDateTime.of(may1, early, london);
System.out.println(departure);

LocalTime from = LocalTime.from(departure);
System.out.println(from);

ZonedDateTime landing = ZonedDateTime.of(may1, LocalTime.of(11, 35), ZoneId.of("Europe/Stockholm"));

Duration flightLength = Duration.between(departure, landing);
System.out.println(flightLength);

ZonedDateTime now = ZonedDateTime.now();
Duration timeHere = Duration.between(landing, now);
System.out.println(timeHere);

Гэты код прывядзе да падобнага вываду:


2016-05-01T08:45+03:00[Europe/Minsk]
08:45
PT3H50M
PT655H49M49.245S

Калекцыі

Streaming API

Streaming API – гэта яшчэ адзін (у дадатак да лямбда-выразаў) зрух у Java 8 у бок дэкляратыўнага, а не імпэратыўнага праграмаваньня, калі мы пазначаем пажаданы вынік замест рэалізацыі кожнай дробнай дэталі дасягненьня гэтага выніку. Напрыклад, мы кажам мове, што мы жадаем атрымаць адсартаваны ці адфільтраваны сьпіс элемэнтаў, пазначаючы толькі пэўныя парамэтры таго, якім чынам яны павінныя быць адсартаваныя і адфільтраваныя, а ўжо якім чынам іх адсартаваць і адфільтраваць, будзе заклапочана сама мова, прычым яно зробіць гэта аптымізаваным чынам.

Streaming API прызначаны палепшыць працу з калекцыямі элемэнтаў. Плынь (stream) у Java 8 – гэта пасьлядоўнасьць пэўных элемэнтаў, прычым плынь не захоўвае самі элемэнты, але мае спасылку на іх крыніцу і перабірае элемэнтамі ў ходзе выкананьня праграмы. Шмат якія апэрацыі плыняў зроблены такім чынам, што яны ізноў вяртаюць плынь з ужо іншымі характарыстыкамі. Гэта зроблена, каб іх можна было злучаць у ланцуг выклікаў – у так званую трубу:


List ids = invoices.stream()
    .filter(inv -> inv.getCustomer() == Customer.ORACLE)
    .sorted(comparingDouble(Invoice::getAmount))
    .map(Invoice::getId)
    .collect(Collectors.toList());

Усе апэрацыі, якія падтрымлівае інтэрфэйс java.util.stream.Stream падзяляюцца на 2 катэгорыі:

  • такія апэрацыі, як filter, sorted і map, якія могуць злучацца ў ланцуг (трубу).
  • а таксама апэрацыі, як collect, findFirst і allMatch, якія перарываюць ланцуг выклікаў і вяртаюць вынік.

Фільтрацыя

У Streaming API існуе шэраг апэрацыяў для фільтрацыі элемэнтаў, некаторыя зь іх:

Апэрацыя Значэньне
filter Прымае ў якасьці аргумэнту аб'ект Predicate і вяртае плынь, якая будзе ўтрымліваць толькі тыя элемэнты, якія задавальняюць умовам гэтага аб'екту.
distinct Вяртае плынь, якая будзе ўтрымліваць унікальныя элемэнты, у адпаведнасьці з вынікам працы мэтаду equals аб'ектаў у плыні.
limit Вяртае плынь, памер якой ня болей за пазначанае значэньне.
skip Вяртае плынь, выключаючы першыя n-элемэнтаў зыходнай плыні.

List expensiveInvoices = invoices.stream()
    .distinct()
    .filter(inv -> inv.getAmount() > 10000)
    .limit(5)
    .collect(Collectors.toList());

Праверка на адпаведнасьць

Можна праверыць ці адпавядаюць усе/пэўныя/ніякія элемэнты плыні пэўнаму крытэру. Для гэтага ў Streaming API існуюць апэрацыі allMatch, anyMatch і noneMatch. У якасьці парамэтру яны прымаюць Predicate, а вяртаюць вынік тыпу boolean. Прыклад праверкі ці ўсе чэкі маюць суму большую за 1000:


boolean expensive = invoices.stream()
    .allMatch(inv -> inv.getAmount() > 1000);

Адзін з мноства

Калі трэба атрымаць проста любы ці першы элемэнт з плыні, можна скарыстацца апэрацыямі findAny і findFirst. У выніку будзе вернуты аб'ект Optional:


Optional = invoices.stream()
    .filter(inv -> inv.getCustomer() == Customer.ORACLE)
    .findAny();

Mapping

Пры дапамозе мэтаду map можна пераўтварыць кожны элемэнт плыні ў іншы аб'ект. Мэтад прымае ў якасьці аргумэнту аб'ект Function, які пасьлядоўна ўжываецца да кожнага з элемэнтаў плыні, а вынік яго выкананьня фармуе новую плынь. Напрыклад, такім чынам можна атрымаць ID усіх чэкаў, якія ўтрымліваюцца ў зыходнай плыні:


List ids = invoices.stream()
    .map(Invoice::getId)
    .collect(Collectors.toList());

Reducing

Яшчэ адна апэрацыя, якую часта трэба выканаць над шэрагам элемэнтаў – гэта аб'яднаць нейкім чынам гэтыя элемэнты, каб атрымаць у выніку адно значэньне. Напрыклад, вылічыць суму ўсіх чэкаў у плыні. Клясычна мы б вырашалі гэтую задачу наступным чынам:


double sum = 0;
for (Invoice invoice : invoices) {
    sum += invoice.getAmount();
}

У Streaming API існуе мэтад reduce, пры дапамозе якога папярэдні фрагмэнт коду можна перапісаць наступным чынам:


double sum = invoices.stream().map(Invoice::getAmount).reduce(0.0, (a, b) -> a + b);

Альбо яшчэ адзін прыклад, як знайсьці максымальнае значэньне ў плыні лічбаў:


int max = numbers.stream().reduce(Integer.MIN_VALUE, Integer::max);

Collectors

Калі мы скончылі апрацоўваць плынь і трэба вярнуць шэраг яе элемэнтаў, выкарыстоўваецца мэтад collect. У якасьці аргумэнту ён прымае аб'ект java.util.stream.Collector, які апісвае якім чынам трэба сабраць і вярнуць элемэнты. Напрыклад, у ранейшых прыкладах мы ўжо выкарыстоўвалі фабрычны мэтад Collectors.toList(), які вяртае аб'ект Collector з інструкцыямі вярнуць просты сьпіс элемэнтаў:


List invoices = invoices.stream().collect(Collectors.toList());

У клясе Collectors існуюць і іншыя падобныя мэтады, напрыклад:


Map> customerToInvoices = invoices.stream()
    .collect(Collectors.groupingBy(Invoice::getCustomer));

Іншае ў java.util

Optional

Для спрашчэньня ланцуговых выклікаў у функцыянальным стылі, каб пазьбягаць NullPointerException, была уведзена кляса java.util.Optional. Уявім прыклад:


getEventWithId(10).getLocation().getCity();

Калі getEventWithId(10) верне null, будзе кінутае выключэньне. Нават калі не, тады існуе магчымасьць, што наступны выклік, getLocation(), таксама верне null і зноў будзе кінутае выключэньне. Іншымі словамі, кожны з выклікаў у ланцугу можа вярнуць null і будзе кінутае выключэньне. Каб пазьбегнуць гэтага, можна ўбудаваць код, які будзе «абараняць» ад выключэньняў, напрыклад такім чынам:


public String getCityForEvent(int id) {
    Event event = getEventWithId(id);
    if (event != null) {
        Location location = event.getLocation();
        if (location != null) {
            return location.getCity();
        }
    }
    return "TBC";
}

Раней такія праверкі трэба было рабіць ледзь не на кожным кроку. Пачынаючы з Java 8 гэта можна рабіць больш ляканічна:


public String getCityForEvent(int id) {
    Optional.ofNullable(getEventWithId(id))
            .flatMap(this::getLocation)
            .map(this::getCity)
            .orElse("TBC");
}

I/O і NIO

Апрацоўка падзеяў

Рэгулярныя выразы

Сетка

Паралельныя і асынхронныя вылічэньні

Уключае клясы і інтэрфэйсы з герархіі java.util.concurrent.

TimeUnit – пералічэньне для пераводу адных адзінак часу ў іншыя:


TimeUnit.HOURS.toSeconds(10);

Semaphore – кляса для таго, каб абмяжоўваць колькасьць патокаў, якія могуць паралельна выконвацца:


public static void main(final String... args) {
    Runnable limitedCall = new Runnable() {
        final Random rand = new Random();
        final Semaphore semaphore = new Semaphore(3);
        int count = 0;

        public void run() {
            int time = rand.nextInt(15);
            int num = count++;

            try {
                semaphore.acquire();
                System.out.println("Executing " + "long-running action for " + time + " seconds... #" + num);
                Thread.sleep(time * 1000);
                System.out.println("Done with #" + num + "!");
                semaphore.release();
            } catch (InterruptedException intEx) {
                intEx.printStackTrace();
            }
        }
    };

    for (int i=0; i<10; i++)
        new Thread(limitedCall).start();
}

Інтэрфэйс Future<T> – гэта трымальнік значэньня тыпу <T>, характэрнай рысай якога зьяўляецца тое, што значэньне ў агульным выпадку не даступнае да нейкага моманту пасьля стварэньня Future.

Інтэрфэйсы Executor і ExecutorService – гэта нешта, што выконвае задачы. Гэтае нешта і будзе патокам, але інтэрфэйсы хаваюць падрабязнасьці таго, якім чынам паток ажыцьцяўляе выкананьне. Патокі спажываюць адносна шмат рэсурсаў, таму мае сэнс паўторна іх выкарыстоўваць, а не выдзяляць аднакроць з наступным выкідваньнем. Інтэрфэйс ExecutorService спрашчае дзяленьне працы паміж патокамі, а таксама забясьпечвае аўтаматычнае паўторнае выкарыстаньне патокаў, што палягчае праграмаваньне і паляпшае прадукцыйнасьць.


final Collection tasks = new ArrayList();
for (int i = 0; i < 100; i++) {
    tasks.add(new Task());
}
ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
try {
    Collection> results = threadPool.invokeAll(tasks);
} catch (InterruptedException e) {
    e.printStackTrace();
}

public class Task implements Callable {
    public String call() throws Exception {
        return null;
    }
}

У вышэйпрыведзеным прыкладзе, колькі б не было перададзена патокаў для адначасовага выкананьня (100 у прыкладзе), выконвацца будуць толькі столькі, колькі ядраў/працэсараў у кампутары, на якім прыклад выконваецца, астатнія будуць чакаць сваёй чаргі. Такі падыход адназначна не прывядзе да празьмернай нагрузкі на сыстэму.

Java 8: Definitive guide to CompletableFuture

Java 8 Concurrency Tutorial

Java 8 Concurrency Tutorial: Threads and Executors

CompletableFuture

Адной з новых магчымасьцяў Java 8 зьяўляецца кляса java.util.concurrent.CompletableFuture, якая дазваляе рабіць ланцугі выклікаў (камбінаваць некалькі асынхронных выклікаў). У наступным прыкладзе 2 выклікі, якія блякуюць далейшы ход праграмы (атрыманьне цаны і абменнага курсу) робяцца паралельна і асынхронна, і калі абодва вынікі будуць атрыманы, будзе выведзены канчатковы кошт:


findBestPrice("iPhone6")
    .thenCombine(lookupExchangeRate(Currency.GBP), this::exchange)
    .thenAccept(localAmount -> System.out.printf("It will cost you %f GBP\n", localAmount));

private CompletableFuture findBestPrice(String product Name) {
    return CompletableFuture.supplyAsync(() -> priceFinder.findBestPrice(productName));
}

private CompletableFuture lookupExchangeRate(Currency localCurrency) {
    return CompletableFuture.supplyAsync(() -> exchangeService.lookupExchangeRate(Currency.USD, localCurrency));
}

JSE

Common Annotations

@Generated

Пазначае зыходны код, які быў згенераваны:


@Generated(“com.sun.xml.rpc.AProcessor”)
public interface StockQuoteService extends java.rmi.Remote {
  this.context = context;
}

@ManagedBean

Выкарыстоўваецца для пазначэньня аб'ектаў, якія могуць кіравацца кантэйнэрам:


@ManagedBean(“cart”)
public class ShoppingCart {
  ...
}

@PostConstruct і @PreDestroy

@PostConstruct выкарыстоўваецца для пазначэньня мэтаду, які будзе выкліканы пасьля ўсіх іньекцый, але перад тым, як бін будзе прадстаўлены кантэйнэрам для выкарыстаньня. А @PreDestroy выкарыстоўваецца для пазначэньня мэтаду, які будзе выкліканы непасрэдна перад тым, як кантэйнэр вынішчыць бін, для вызваленьня папярэдне занятых рэсурсаў. Мэтады, пазначаныя гэтымі анатацыямі, павінны вяртаць void, не кідаць checked выключэньняў, не быць статычнымі, а таксама не прымаць аргумэнтаў, за выключэньнем EJB-інтэрсэптэраў, калі яны прымаюць аргумэнт InvocationContext.


...

@Resource
private void setMyDB(DataSource ds) {
  myDB = ds;
}

@PostConstruct
private void initialize() {
  // Initialize the connection object from the DataSource
  connection = myDB.getConnection();
}

@PreDestroy
private void cleanup() {
  // Close the connection to the DataSource.
  connection.close();
}

private DataSource myDB;
private Connection connection;

...

@Resource і @Resources

@Resource выкарыстоўваецца для пазначэньня спасылкі на рэсурс. Калі выкарыстоўваецца перад полем альбо сэттэрам, кантэйнэр зробіць іньекцыю адпаведнага значэньня ў час ініцыіраваньня праграмы. Калі выкарыстоўваецца перад клясам, гэта азначае, што праграма будзе "шукаць" значэньне ў часе сваёй працы.


@Resource(name=”customerDB”)
private DataSource myDB;

Калі ж трэба пазначыць некалькі рэсурсаў, выкарыстоўваецца @Resources:


@Resources ({
  @Resource(name=”myDB” type=javax.sql.DataSource),
  @Resource(name=”myMQ” type=javax.jms.ConnectionFactory)
})
public class CalculatorBean {
  //...
}

@DeclareRoles

Выкарыстоўваецца для аб'яўленьня роляў праграмы. (ня вельмі разумею сэнс)


@DeclareRoles("BusinessAdmin")
public class Calculator {
  public void convertCurrency() {
    if (x.isUserInRole(“BusinessAdmin”)) {
    ....
    }
  }
  ...
}                    

@RolesAllowed

Выкарыстоўваецца для пазначэньня роляў, пад якімі дазваляецца выкананьне мэтадаў. Можа стаяць перад клясай і перад мэтадам. Калі ўжываецца перад клясай, ужываецца да ўсіх яе мэтадаў. Калі ўжываецца і перад клясай, і перад мэтадамі, тады анатацыя перад мэтадам перакрывае анатацыю перад клясай.


@RolesAllowed("Users")
public class Calculator {

  @RolesAllowed(“Administrator”)
  public void setNewRate(int rate) {
    ...
  }

  ...
}

@PermitAll

Выкарыстоўваецца каб пазначыць, што выкананьне мэтадаў дазваляецца любым ролям. Можа стаяць перад клясай і перад мэтадам.


@RolesAllowed("Users")
public class Calculator {

  @RolesAllowed(“Administrator”)
  public void setNewRate(int rate) {
    ...
  }

  @PermitAll
  public long convertCurrency(long amount) {
    ...
  }

  ...
}

@DenyAll

Выкарыстоўваецца каб пазначыць, што выкананьне мэтадаў забараняецца незалежна ад ролі. Можа стаяць перад клясай і перад мэтадам.


@RolesAllowed("Users")
public class Calculator {

  @RolesAllowed(“Administrator”)
  public void setNewRate(int rate) {
    ...
  }

  @DenyAll
  public long convertCurrency(long amount) {
    ...
  }
  
  ...
}

@DataSourceDefinition і @DataSourceDefinitions

@DataSourceDefinition выкарыстоўваецца для аб'яўленьня DataSource кантэйнэра і для рэгістрацыі яго ў JNDI.


@DataSourceDefinition(name="java:global/MyApp/MyDataSource",
    className="org.apache.derby.jdbc.ClientDataSource",
    url="jdbc:derby://localhost:1527/myDB",
    user="lance",
    password="secret")

альбо:


@DataSourceDefinition(name="java:global/MyApp/MyDataSource",
    className="com.foobar.MyDataSource",
    portNumber=6689,
    serverName="myserver.com",
    user="lance",
    password="secret")

Калі ж трэба аб'явіць некалькі DataSource, выкарыстоўваецца @DataSourceDefinitions:


@DataSourceDefinitions ({
  @DataSourceDefinition(name="java:global/MyApp/MyDataSource",
      className="com.foobar.MyDataSource",
      portNumber=6689,
      serverName="myserver.com",
      user="lance",
      password="secret"),
  @DataSourceDefinition(name="java:global/MyApp/MyDataSource",
      className="org.apache.derby.jdbc.ClientDataSource",
      url="jdbc:derby://localhost:1527/myDB",
      user="lance",
      password="secret")
})
public class CalculatorBean {
  ...
}

JDBC

 

 

JNDI

 

 

JAXP

 

 

JAXB

 

 

StAX

 

 

JAAS

 

 

JMX

 

 

JAF

 

 

JEE

CUBA Platform

Рэсурсы

Books on Java EE and Related Technologies

Screencasts: Adam Bien (у храналягічным парадку)

IntelliJ IDEA: JavaEE 7 Screencasts

JavaEE 7 Samples

Java Brains Screencasts

Java Code Geeks Tutorials

ZEEF: Arjan Tijms, Abhishek Gupta

Спэцыфікацыі

Зялёным тлом пазначаныя спэцыфікацыі, якія ўвайшлі ў Web Profile (JEE 6 і JEE 7) – гэта падмноства спэцыфікацыяў з JEE, актуальнае для распрацоўкі сеціўных праграмаў.

Тэхналёгія J2EE 1.4 (11.11.03)
JSR 151 | Tutorial
JEE 5 (11.05.06)
JSR 244 | Tutorial
JEE 6 (10.12.09)
JSR 316 | Tutorial
JEE 7 (16.06.13)
JSR 342 | Tutorial
Вэрсія JSR Вэрсія JSR Вэрсія JSR Вэрсія JSR
Тэхналёгіі сеціўных праграмаў:
Java Servlet 2.4 JSR 154 2.5 JSR 154 3.0 JSR 315 3.1 JSR 340
JSF 1.1 JSR 127 1.2 JSR 252 2.0 JSR 314 2.2 JSR 344
EL 2.2 JSR 245 3.0 JSR 341
JSP 2.0 JSR 152 2.1 JSR 245 2.2 JSR 245 2.3 JSR 245
JSTL 1.1 JSR 52 1.2 JSR 52 1.2 JSR 52 1.2 JSR 52
Java API for WebSocket 1.0 JSR 356
Тэхналёгіі enterprise праграмаў:
Dependency Injection 1.0 JSR 330 1.0 JSR 330
Contexts and Dependency Injection 1.0 JSR 299 1.1 JSR 346
Bean Validation 1.0 JSR 303 1.1 JSR 349
EJB 2.1 JSR 153 3.0 JSR 220 3.1 JSR 318 3.2 JSR 345
JPA 1.0 JSR 220 2.0 JSR 317 2.1 JSR 338
JTA 1.0 1.1 JSR 907 1.1 JSR 907 1.2 JSR 907
JMS 1.1 1.1 JSR 914 1.1 JSR 914 2.0 JSR 343
JavaMail API 1.3 1.4 JSR 919 1.4 JSR 919 1.5 JSR 919
JCA 1.5 JSR 112 1.5 JSR 112 1.6 JSR 322 1.7 JSR 322
Concurrency Utilities for Java EE 1.0 JSR 236
Тэхналёгіі сеціўных сэрвісаў:
Web Services 1.0 1.2 JSR 109 1.3 JSR 109 1.3 JSR 109
Web Services Metadata 2.0 JSR 181 2.1 JSR 181 2.1 JSR 181
JAX-RS 1.1 JSR 311 2.0 JSR 339
JSON-P 1.0 JSR 353
JAX-WS 2.0 JSR 224 2.2 JSR 224 2.2 JSR 224
JAX-RPC 1.1 1.1 JSR 101 1.1 JSR 101 1.1 JSR 101
JAXM 1.1 1.3 JSR 67 1.3 JSR 67
JAXR 1.0 1.0 JSR 93 1.0 JSR 93 1.0 JSR 93
Тэхналёгіі JSE, якія маюць дачыненьне да JEE:
Common Annotations 1.0 JSR 250 1.1 JSR 250 1.2 JSR 250
JDBC 3.0 JSR 54 3.0 JSR 54 4.0 JSR 221 4.1 JSR 221
JNDI 1.2
JAAS 1.0 1.0 1.0 1.0
JAXB 2.0 JSR 222 2.2 JSR 222 2.2 JSR 222
JAXP 1.2 1.2 1.2 1.3 JSR 206
StAX 1.0 JSR 173 1.0 JSR 173 1.0 JSR 173
JMX 1.2 JSR 3
JAF 1.0 1.1 JSR 925 1.1 JSR 925 1.1 JSR 925

Дэскрыптары разгортваньня

my-app
`-- src
    `-- main
        |-- java
        |    `...
        |-- resources
        |   |
        |   `--META-INF
        |      |-- application.xml           - JavaEE
        |      |-- application-client.xml    - JavaEE
        |      |-- persistence.xml           - JPA
        |      `-- ra.xml                    - JCA
        `-- webapp
            |
            `--WEB-INF
               |-- beans.xml                 - CDI
               |-- ejb-jar.xml               - EJB
               |-- faces-config.xml          - JSF
               |-- validation.xml            - Beans Validation
               |-- web.xml                   - Servlet
               |-- web-fragment.xml          - Servlet
               `-- webservices.xml           - Web-services SOAP
        

web.xml


<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
</web-app>

beans.xml


<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" 
    bean-discovery-mode="all">
</beans>

faces-config.xml


<faces-config version="2.2"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
</faces-config>

persistence.xml


<persistence version="2.1"
   xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
   <persistence-unit name="primary">
      <!-- If you are running in a production environment, add a managed 
        data source, this example data source is just for devleopment and testing! -->
      <!-- The datasource is deployed as WEB-INF/hibernate4-quickstart-ds.xml, you can
        find it in the source at src/main/webapp/WEB-INF/hibernate4-quickstart-ds.xml -->
      <jta-data-source>java:jboss/datasources/Hibernate4QuickstartDS</jta-data-source>
      <properties>
         <!-- Properties for Hibernate -->
         <property name="hibernate.hbm2ddl.auto" value="create-drop" />
         <property name="hibernate.show_sql" value="false" />
      </properties>
   </persistence-unit>
</persistence>

Біны і CDI

Амаль любая Java-кляса з канструктарам без парамэтраў, альбо з канструктарам пазначаным анатацыяй @Inject, зьяўляецца кампанэнтам, кіруемым кантэйнэрам (managed bean). Акрамя патрабаваньня да канструктараў ёсьць яшчэ некалькі патрабаваньняў да кіруемых біноў:

  • яны павінны быць верхняга ўзроўню (top-level), то бок не nested;
  • ня могуць быць не-статычнымі ўнутранымі клясамі (з унутраных клясаў толькі статычныя могуць быць кіруемымі бінамі);
  • яны павінны быць пэўнымі клясамі (не абстрактнымі), альбо мець анатацыю @Decorator;
  • не павінны быць пазначаныя як EJB праз адпаведную анатацыю, альбо праз файл ejb-jar.xml.

Калі кляса адпавядае ўсім вышэйпералічаным патрабаваньням, яе жыцьцёвым цыклям можа кіраваць кантэйнэр, а таксама рабіць іньекцыю (inject) у яе іншых клясаў ці біноў.

Жыцьцёвы цыкл кампанэнтаў

Вобласьці бачнасьці і кантэкст

Кожны экзэмпляр кіруемага біна, які быў створаны кантэйнэрам праз службу CDI, зьяўляецца кантэкстуальным экзэмплярам, то бок ён існуе выключна ў межах пэўнага кантэксту і доступ да яго маюць толькі тыя іншыя аб'екты, якія існуюць у межах таго ж самага кантэксту. Кантэйнэр аўтаматычна стварае экзэмпляр кіруемага біна, калі нейкі іншы аб'ект мае ў ім патрэбу. Кантэйнэр разбурае раней створаны ім экзэмпляр, калі той кантэкст заканчваецца.

Усяго існуе 5 убудаваных кантэкстаў:

Кантэкст Анатацыя Працягласьць
Запросу @RequestScoped Узаемадзеяньне аднаго карыстальніка з праграмай цягам аднаго HTTP-запыту.
Сэсіі @SessionScoped Узаемадзеяньне аднаго карыстальніка з праграмай цягам шэрагу HTTP-запыту, якія складаюць адну сэсію з праграмай.
Праграмы @ApplicationScoped Падзяляе стан паміж усімі карыстальнікамі праграмы цягам усяго жыцьця праграмы.
Залежны @Dependent Змоўчны кантэкст, калі яўна не пазначаны. Азначае, што аб'ект створаны, каб служыць дакладна аднаму кліенту (іншаму біну), і жыве столькі ж, колькі і кліент.
Дыялёгу @ConversationScoped Працягласьць гэтага кантэксту пашыраецца на шэраг HTTP-запытаў аднаго карыстальніка. Дакладная працягласьць гэтага шэрагу вызначаецца распрацоўшчыкам, але не можа "перасякаць" межы адной сэсіі.

Request scoped біны альбо залежныя біны, чый кліент зьяўляецца request scoped біном, могуць не быць serizalizable. Астатнія кантэкстуальныя біны (кантэксту сэсіі, праграмы, дыялёгу, а таксама залежныя ад такіх біноў) павінны быць serizalizable.

@Inject

Як мы ўжо пазначылі вышэй, кантэйнэр акрамя кіраваньня жыцьцём біна можа ўбудоўваць (рабіць іньекцыю) у яго неабходныя яму рэсурсы альбо іншыя біны. Што можа ўбудоўвацца:

  • амаль любая Java-кляса;
  • Session beans;
  • JavaEE-рэсурсы: крыніцы даных (data sources), JMS-topics, чэргі, фабрыкі злучэньняў (conncetion factories) і падобнае;
  • persistent conexts (аб'екты EntityManager з JPA);
  • палі-вытворцы (producer fields);
  • аб'екты, якія вяртаюцца мэтадамі-вытворцамі (producer methods);
  • спасылкі на вэб-сэрвісы;
  • спасылкі на адлеглыя EJB.

Іньекцыя робіцца пры дапамозе анатацыі @Inject:


import javax.inject.Inject;
public class Printer {
    @Inject Greeting greeting;
    ...
}

У дадзеным прыкладзе аб'ект Greeting будзе аўтаматычна падстаўлены (створаны новы альбо ўзяты існуючы, калі ў дадзеным кантэксьце ён ужо існуе) у поле greeting ствараемага экзэмпляра клясы Printer. Пры гэтым нават не абавязкова мець public setter да гэтага поля, кантэйнэр зробіць іньекцыю нават у прыватнае поле.

Кваліфікатары

Кантэйнэр спрабуе знайсьці кандыдата для іньекцыі па тыпу аб'екта. Калі ж кантэйнэр знаходзіць некалькі раўназначных кандыдатаў, ён кіне выключэньне. У наступным прыкладзе кантэйнэр ня здолее выбраць паміж SuperCar і CrossOver для іньекцыі ў поле car і кіне выключэньне:


public class SuperCar implements Car {...}

public class Crossover implements Car {...}

public class Garage {
    @Inject Car car;
}

Для таго, каб вырашыць гэтую праблему, служаць кваліфікатары – анатацыі-маркеры, якія аб'яўляюцца пры дапамозе анатацыі @Qualifier:


@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Parkable {}

Тады вышэйзгаданы канфліктны прыклад можна перапісаць наступным чынам:


public class SuperCar implements Car {...}

@Parkable
public class Crossover implements Car {...}

public class Garage {
    @Inject @Parkable Car car;
}

Тым самым мы кажам кантэйнэру, што для іньекцыі ў дадзеным месцы падыходзіць не любы Car, але толькі той, які пазначаны анатацыяй-маркерам @Parkable. Кантэйнэр здолее адназначна выбраць адзін кандыдат для іньекцыі і выключэньня ня будзе.

Кваліфікатары можна камбінаваць, то бок пазначаць адну і тую ж клясу ці месца іньекцыі некалькімі кваліфікатарамі. Акрамя гэтага іх можна парамэтрызаваць. Прывядзем ніжэй 2 варыянты рэалізаваць адну і тую ж задуму. Першы варыянт пры дапамозе множных кваліфікатараў:


@Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Number {}

@Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Odd {}

public class Game {
    @Inject @Number @Odd int num;
    ...
}

Другі варыянт пры дапамозе аднаго кваліфікатара, але з парамэтрамі:


@Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Number {
    boolean odd();
}

public class Game {
    @Inject @Number(odd = true) int num;
    ...
}

@Default

@Default – гэта ўбудаваны і пры гэтым змоўчны кваліфікатар. То бок, калі пры аб'яўленьні біна не пазначана ніякага яўнага кваліфікатара, ён аўтаматычна пазначаецца кваліфікатарам @Default. Калі ў момант убудаваньня залежнасьці, кантэйнэр вызначыць некалькі кандыдатаў, пры гэтым усе акрамя аднаго будуць пазначаныя яўнымі кваліфікатарамі, а адзін не будзе пазначаны, а таксама ў пункце ўбудаваньня не будзе пазначана яўнага кваліфікатара, тады кантэйнэр ня кіне выключэньне і падставіць @Default-кандыдата.

@Alternative

Анатацыяй @Alternative можна пазначыць не прыярытэтны, а наадварот непажаданы кандыдат(-аў) для іньекцыі:


@Alternative
public class SuperCar implements Car {...}

public class Crossover implements Car {...}

public class Garage {
    @Inject Car car;
}

Адзіны спосаб, каб кантэйнэр усё ж мог яго выбраць для іньекцыі – актываваць яго ў дэскрыптары beans.xml:


<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       version="1.1" 
       bean-discovery-mode="all">
  <alternatives>
    <class>some.package.SuperCar</class>
  </alternatives>
</beans>

@Vetoed

Можна таксама ўвогуле выключыць нейкую клясу пры абраньні кандыдатаў для іньекцыі праз анатацыю @Vetoed.

@Any і выбар з альтэрнатываў

Анатацыяй @Any звычайна пазначаецца месца іньекцыі і кажа кантэйнэру, каб ён убудаваў усе магчымыя кандыдаты (сьпіс). Пазьней можна прабягацца па гэтаму сьпісу і выбіраць патрэбны кандыдат у рантайм:


@Named("atm")
public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {

    @Inject @Any
    private Instance allTransports;
    private ATMTransport transport;

    private boolean useJSON = true;
    private boolean behindFireWall = true;

    @PostConstruct
    protected void init() {
        ATMTransport soapTransport, jsonTransport, standardTransport;
        standardTransport = allTransports.select(new AnnotationLiteral() {}).get();
        jsonTransport = allTransports.select(new AnnotationLiteral() {}).get();
        soapTransport = allTransports.select(new AnnotationLiteral() {}).get();

        if (!behindFireWall) {
            transport = standardTransport;
        } else {
            if (useJSON) {
                transport = jsonTransport;
            } else {
                transport = soapTransport;
            }
        }
    }
}

@Named

Анатацыяй @Named пазначаюцца біны, да якіх павінны мець доступ вью па іх імёнах:


@Named
@RequestScoped
public class Game implements Serializable {
    ...
}

<!DOCTYPE html>
<head>...</head>
<body>
 <div>
  Your guess:
  <h:inputText id="inputGuess" value="#{game.guess}"
     required="true" size="3"
     disabled="#{game.number eq game.guess}"
     validator="#{game.validateNumberRange}" />
  <h:commandButton id="guessButton" value="Guess"
     action="#{game.check}"
     disabled="#{game.number eq game.guess}" />
 </div>
</body>
</html>

Улічваючы, што змоўчным кантэкстам (ці вобласьцю бачнасьці) біна, калі яўна не пазначана, зьяўляецца @Dependent, што не падыходзіць у якасьці мадэлі для вью (таму што даныя, якія туды трапілі з вью, будуць адразу страчаныя), і разам з анатацыяй @Named заўсёды прыходзіцца пазначаць анатацыю @RequestScoped, стандартам CDI была ўведзеная дапаможная анатацыя @Model, якая проста камбінуе @Named і @RequestScoped:


@Model
public class Game implements Serializable {
    ...
}

@New

Калі ў месцы ўбудаваньня залежнасьці пажадана мець заўсёды ўнікальны аб'ект, які ня будзе падзяляцца з іншымі кантэкстамі (аб'ектамі), можна выкарыстоўваць анатацыю @New. У гэтым выпадку кантэйнэр будзе заўсёды ствараць новы аб'ект, а не спрабаваць падставіць ужо існуючы.

@Produces і @Disposes

Анатацыя @Produces прадстаўляе мэханізм рабіць іньекцыі ня толькі біноў і JEE-рэсурсаў. Такім чынам могуць быць іньектаваны:

  • прымітыўныя тыпы (int, boolean і інш.);
  • масівы і калекцыі;
  • аб'екты кшталту java.util.Date альбо ja­va.lang.String;
  • аб'екты, чыя дакладная кляса будзе вядома толькі ў часе выкананьня праграмы;
  • аб'екты, якія патрабуюць дадатковай ініцыялізацыі.

Некалькі прыкладаў:


@Qualifier @Retention(RUNTIME) @Target({ TYPE, METHOD, PARAMETER, FIELD })
public @interface Random {}

@ApplicationScoped
public class Generator implements Serializable {
    private java.util.Random random = new java.util.Random(System.currentTimeMillis());
    private int maxNumber = 100;

    @Produces @Random // Гэты мэтад стварае экзэмпляр Random
    int next() {
        return random.nextInt(maxNumber - 1) + 1;
    }
}

@SessionScoped
public class Game implements Serializable {

    @Inject @Random // У гэтае поле падстаўляецца папярэдне створаны экзэмпляр Random
    Instance randomNumber; // Instance - гэта сьпіс, які мае ітэратар. У агульным
                                    // выпадку кандыдатаў на падстаноўку можа быць некалькі
                                    // (некалькі @Default), тады сюды падставіцца не адно
                                    // значэньне, а ўсе знойдзеныя. Можна рабіць так
                                    // randomNumber.iterator().next()
    ...
}

...

@Produces
public List getGreetings() {
List response = new ArrayList();
    ...
    return response;
}

@Inject List list;

...

@Produces-мэтад можа ў якасьці аргумэнту прымаць InjectionPoint – доступ да асяродку іньекцыі:


class Loggers {
    @Produces Logger getLogger(InjectionPoint injectionPoint) {
        return Logger.getLogger( injectionPoint.getMember().getDeclaringClass().getSimpleName() );
    }
}

@SessionScoped
public class Permissions implements Serializable {
    @Inject Logger log; // Так можна рабіць у кожным месцы, дзе патрэбны логер
                        // і кожны раз будзе стварацца логер для адпаведнай клясы
    ...
}

З-за таго, што ў выпадку мэтаду-вытворцы кантэйнэр губляе кіраваньне вырашэньнем залежнасьцей і іх вобласьці бачнасьці, трэба быць уважлівым, каб не парушыць цэльнасьць. Разгледзім такі прыклад:


@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy(CreditCardPaymentStrategy ccps,
                                          CheckPaymentStrategy cps,
                                          PayPalPaymentStrategy ppps) {
    switch (paymentStrategy) {
        case CREDIT_CARD: return ccps;
        case CHEQUE: return cps;
        case PAYPAL: return ppps;
        default: return null;
    }
}

Калі адзін ці некалькі аргумэнтаў мэтаду getPaymentStrategy будуць request-scoped, у пэўны момант жыцьця сэсіі адпаведныя аб'екты ўжо могуць не існаваць. Каб пазьбегнуць такой сытуацыі, трэба даваць максымальны кантроль кантэйнэру, які дакладна не дапусьціць яе:


@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy(@Dependent CreditCardPaymentStrategy ccps,
                                          @Dependent CheckPaymentStrategy cps,
                                          @Dependent PayPalPaymentStrategy ppps) {
    switch (paymentStrategy) {
        case CREDIT_CARD: return ccps;
        case CHEQUE: return cps;
        case PAYPAL: return ppps;
        default: return null;
    }
}

У дадатак да асаблівага мэханізму стварэньня аб'екта ў CDI існуе асаблівы мэханізм да разбурэньня раней створанага кантэйнэрам аб'екта – анатацыя @Dispose. Гэты спосаб патрэбны ў тым выпадку, калі аб'ект павінен быць ня проста выкінуты, але патрэбна яшчэ вызваліць адкрытыя ім рэсурсы:


@Produces @RequestScoped
Connection connect(User user) {
    return createConnection(user.getId(), user.getPassword());
}

void close(@Disposes Connection connection) {
    connection.close();
}

Падзеі

CDI-падзеі прадстаўляюць спосаб камунікацыі паміж кампанэнтамі праграмы без убудаваньня залежнасьцяў.

У вытворцы падзеяў робіцца іньекцыя падзеяў і выклікаецца мэтад fire, каб даслаць іх слухацелям падзеяў:


@Inject Event event;
...
event.fire(new LoggedInEvent(username));

Слухацелі аб'яўляюць мэтад, які будзе прымаць дасланыя падзеі:


void onLoggedIn(@Observes LoggedInEvent event) {
    ...
}

Па змоўчваньні, калі дасылаецца падзея, а адпаведны экзэмпляр слухацеля(-ў) яшчэ ня быў створаны, кантэйнэр яго створыць. Гэта можа быць не пажадана. Таму слухацель можа быць умоўным:


public void refreshOnDocumentUpdate(@Observes(receive=IF_EXISTS) @Updated Document doc) {
    ...
}

Перахопнікі

Перахопнікі выкарыстоўваюць для ўбудаваньня скразнога функцыяналу скрозь аб'екты рознага тыпу і прызначэньня. Напрыклад, лагіраваньне альбо функцыянал па бясьпецы. Робіцца гэта для таго, каб ён утрымліваўся ў адным месцы, а ня быў раскіданы па ўсяму праекту. Аб'ектамі, у якія можна ўстаўляць перахопнікі, зьяўляюцца любыя кіруемыя біны – CDI-біны, EJB, RESTful-сэрвісы і іншае.

Іншымі словамі перахопнікі – гэта клясы, чые мэтады выклікаюцца, калі выклікаюцца мэтады мэтавых клясаў, альбо адбываюцца падзеі жыцьцёвага цыкла гэтых клясаў, альбо адбываюцца таймаўты EJB-мэтадаў.

Каб убудаваць перахопнік, спачатку трэба стварыць злучальнік паміж перахопнікам і бізнэс-клясай. Для гэтага служыць анатацыя @InterceptorBinding:


@Inherited @Retention(RUNTIME) @Target({METHOD, TYPE})
@InterceptorBinding
public @interface Logging {
}

Пасьля гэтага ствараем клясу самога перахопніка і аб'яўляем у ёй мэтады-перахопнікі (ня больш за 1 кожнага тыпу):


@Interceptor
@Logging // Гэта злучальнік, які мы аб'явілі раней
public class LoggingInterceptor {
    @AroundInvoke
    public Object log(InvocationContext context) throws Exception {
        String name = context.getMethod().getName();
        String params = context.getParameters().toString();
        //. . .
        return context.proceed(); // Выклік бізнэс-лёгікі
    }
}

І напрыканцы пазначаем анатацыяй злучальніка бізнэс-клясу (тады перахоплівацца будуць усе мэтады бізнэс-клясы):


@Logging
public class SimpleGreeting {
    ...
}

Альбо толькі пэўны мэтад(ы) бізнэс-клясы (тады будуць перахоплівацца толькі пазначаныя мэтады):


public class SimpleGreeting {
    @Logging
    public String greet(String name) {
        ...
    }
}

Мэтады-перахопнікі бываюць наступных тыпаў:

  • @AroundConstruct – перахопнікі канструктараў;
  • @AroundInvoke – перахопнікі мэтадаў;
  • @PostConstruct і @PreDestroy – перахопнікі падзеяў жыцьцёвага цыкла;
  • @AroundTimeout – перахопнікі таймаўтаў EJB-мэтадаў;

Па змоўчваньні перахопнікі не актыўныя, каб актываваць іх, трэба дадаць адпаведныя інструкцыі ў дэскрыптар beans.xml:


<beans xmlns='http://java.sun.com/xml/ns/javaee'>
  <interceptors>
    <class>org.example.TransactionInterceptor</class>
    <class>org.example.LoggingInterceptor</class>
  </interceptors>
</beans>

Такія перахопнікі будуць актыўныя для архіву, які будзе ўтрымліваць гэты дэскрыптар. Альтэрнатыўным спосабам актываваць перахопнік зьяўляецца ўжываньне анатацыі @Priority пры яго аб'яўленьні:


@Priority(Interceptor.Priority.APPLICATION + 10)
@Interceptor
@Logging
public class LoggingInterceptor {
    ...
}

Перахопнік, актываваны такім чынам, будзе актыўным ува ўсёй праграме, незалежна ад таго, у якім архіве ён утрымліваецца. Перадвызначаныя канстанты для прыярытэтаў:

Прыярытэт Значэньне
Interceptor.Priority.PLATFORM_BEFORE 0
Interceptor.Priority.LIBRARY_BEFORE 1000
Interceptor.Priority.APPLICATION 2000
Interceptor.Priority.LIBRARY_AFTER 3000
Interceptor.Priority.PLATFORM_AFTER 4000

Чым меншае значэньне, тым раней у чарадзе перахопнікаў ён будзе выкліканы, калі некалькі перахопнікаў вызначаныя для аднаго і таго ж мэтаду. Калі ж перахопнікі актываваныя праз дэскрыптар, тады пасьлядоўнасьць іх выклікаў вызначаецца пасьлядоўнасьцю, зь якой яны зьмяшчаюцца ў дэскрыптары.

Дэкаратары

Сутнасьць дэкаратараў падобна да сутнасьці перахопнікаў, але калі перахопнікі ня ведаюць і не турбуюцца пра бізнеэс-лёгіку, якую яны абгортваюць, то дэкаратары ствараюцца для аб'ектаў пэўнага тыпу і для пашырэньня іх бізнэс-лёгікі. Дэкаратар – гэта бін, які рэалізуе дэкаруемы ім бін (інтэрфэйс) і пазначаецца стэрэатыпнай анатацыяй @Decorator


@Decorator 
class TimestampLogger implements Logger {

    @Inject @Delegate Logger logger;

    public void log(String message) {
        logger.log( timestamp() + ': ' + message);
    }
}

Дэкаратар павінен рэалізоўваць адзіны пункт іньекцыі дэлегата – біна, які ён дэкарыруе (радок 4). Усе выклікі мэтадаў дэлегата, якія рэалізуе дэкаратар, будуць накіраваны да дэкаратара, які ў сваю чаргу можа рабіць выклік адпаведных мэтадаў дэлегата (радок 7):


@Decorator 
class TimestampLogger implements Logger {

    @Inject @Delegate Logger logger;

    public void log(String message) {
        logger.log( timestamp() + ': ' + message);
    }
}

Дэкаратар можа рэалізоўваць ня ўсе мэтады дэлегата і, адпаведна, быць абстрактным. Дэкаратары выклікаюцца пасьля перахопнікаў.

Дэкаратары, як і перахопнікі, па змоўчваньні не актыўныя, каб актываваць іх, трэба дадаць адпаведныя інструкцыі ў дэскрыптар beans.xml:


<beans xmlns='http://java.sun.com/xml/ns/javaee'>
  <decorators>
    <class>org.example.TimestampLogger</class>
  </decorators>
</beans>

Альбо пазначыць іх анатацыяй @Priority.

Стэрэатыпы

Стэрэатыпы – гэта мэта-анатацыі, якія аб'ядноўваюць іншыя анатацыі. Напрыклад, перадвызначаны стэрэатып @Model:


@Named
@RequestScoped
@Stereotype
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface Model {}

Ён аб'ядноўвае анатацыі @Named і @RequestScoped.

AOP

CDI AOP Tutorial

Валідацыя біноў

 

 

 

EJB

Enterprise JavaBeans 3.1 with Contexts and Dependency Injection: The Perfect Synergy

 

 

JPA

JPA Reference

JPA Tutorial

JPA Mini Book

Hibernate Tutorial

 

JSF

JSF2: How to Create a Global Ajax Status Indicator

Што можна наладжваць праз faces-config.xml

Разработка JSF приложений при помощи IntelliJ Idea. Часть 2: Разработка простого приложения

prettyfaces – The open-source /url/#{rewriting} solution for Servlet, JSF, and Java EE

PrimeFaces

OmniFaces

JAX-RS

JSON Tutorial

GET / POST with RESTful Client API

Java EE 7: Using JAX-RS Client API to consume RESTful Web Services

У ніжэй прыведзеным прыкладзе пры звароце да RESTful GET-рэсурсу адбудзецца аўтаматычнае канвэртацыя Java-аб'екту ў JSON-фармат пры дапамозе RESTEasy. Галоўнае, каб бін Member быў пазначаны JAXB-анатацыяй @XmlRootElement.


@GET
@Produces(MediaType.APPLICATION_JSON)
public List listAllMembers() {
    return repository.findAllOrderedByName();
}

Security

Best practice for REST token-based authentication with JAX-RS and Jersey

Java EE 7 / JAX-RS 2.0: Simple REST API Authentication & Authorization with Custom HTTP Header

Simple Java EE (JSF) Login Page with JBoss PicketLink Security

Is your web application secure? HTTP attacks are real, and dangerous

Security Module of DeltaSpike

@ServletSecurity

Анатацыя ўзроўню клясы @ServletSecurity, якая прызначана абараняць доступ да сэрвлетаў, мае наступнае вызначэньне:


@ServletSecurity(
    httpMethodConstraints = <HttpMethodConstraint[]>,
    value = <HttpConstraint>
)

Дзе атрыбут httpMethodConstraints вызначае абмежаваньні для HTTP-мэтадаў, а атрыбут value вызначае абмежаваньні для астатніх HTTP-мэтадаў, якія не былі вызначаныя ў атрыбуце httpMethodConstraints.

Прыклады

Адсутнасьць якіх-кольвечы абмежаваньняў бясьпекі:


@WebServlet("/process")
@ServletSecurity
public class MyServlet extends HttpServlet {
    // servlet code...
}

Пазначае неабходнасьць кадаваньня для ўсіх HTTP-мэтадаў:


@WebServlet("/process")
@ServletSecurity(@HttpConstraint(transportGuarantee = TransportGuarantee.CONFIDENTIAL))
public class MyServlet extends HttpServlet {
    // servlet code...
}

Забараняе любыя HTTP POST мэтады (адпаведна астатнія HTTP-мэтады дазваляюцца):


@WebServlet("/process")
@ServletSecurity(
    httpMethodConstraints = @HttpMethodConstraint(value = "POST",
        emptyRoleSemantic = EmptyRoleSemantic.DENY)
)
public class MyServlet extends HttpServlet {
    // servlet code...
}

Патрабуе, каб карыстальнік, які робіць запыт да сэрвлету, меў ролю admin (для ўсіх HTTP-мэтадаў):


@WebServlet("/manage")
@ServletSecurity(@HttpConstraint(rolesAllowed = "admin"))
public class AdminServlet extends HttpServlet {
    // servlet code...
}

Патрабуе, каб карыстальнік, які робіць запыт да POST і GET мэтадаў сэрвлету, меў ролю admin. Дадаткова пазначае неабходнасьць кадаваньня для ўсіх POST-мэтаду:


@WebServlet("/manage")
@ServletSecurity(
    httpMethodConstraints = {
        @HttpMethodConstraint(value = "GET", rolesAllowed = "admin"),
        @HttpMethodConstraint(value = "POST", rolesAllowed = "admin",
            transportGuarantee = TransportGuarantee.CONFIDENTIAL),      
    }
)
public class AdminServlet extends HttpServlet {
    // servlet code...
}

JSON Web Token

Рэалізацыі:

PicketLink – гэта фрэймворк бясьпекі JEE праграмаў. Некаторыя з магчымасьцяў:

  • першаклясная падтрымка CDI;
  • магчымасьць кіраваць бясьпекай біноў і іх мэтадаў, вьюх, сэрвлэтаў і REST-сэрвісаў;
  • API для кіраваньня карыстальнікамі, ролямі і групамі;
  • сацыяльны лагін праз Facebook, Twitter, Google+.

Падыходзіць для выпадкаў, калі аўтары праграмы рэалізуюць уласную сыстэму бясьпекі – PicketLink прадстаўляе гатовыя цагліны, зь якіх такую сыстэму можна пабудаваць.

KeyCloak

Fetch Logged In Username in a webapp secured with Keycloak.

SECURITY WITH MICROSERVICES: PROGRAMMATIC SECURITY WITH KEYCLOAK.

SECURING JAX-RS: KEYCLOAK, CDI AND EJB CONFUSION.

TESTING KEYCLOAK INTEGRATION WITH ARQUILLIAN.

PROGRAMMATICALLY ADDING USERS IN KEYCLOAK.

Keycloak examples.

KeyCloak – гэта гатовы-да-выкарыстаньня-з-каробкі Single-Sign-On сэрвэр для мабільных, сеціўных і REST-праграмаў з адкрытым зыходным кодам ад JBoss. Ён прадстаўляе магчымасьці: 1. цэнтральнага кіраваньня карыстальнікамі, ролямі, групамі, сэсіямі; 2. сродкі аўтэнтыфікацыі і аўтарызацыі; 3. сацыяльны лагін.

Усталёўка

Для прастаты у межах дадзенага даведніка разгледзім усталёўку выключна дадатку да WildFly. Па астатнія магчымасьці ўсталёўкі глядзіце афіцыйны даведнік.

  1. Першым крокам сьцягваем архіў апошняй вэрсіі з афіцыйнай старонкі: http://keycloak.jboss.org/downloads. Дадатак да WildFly мае ў сваім імені слова overlay, напрыклад keycloak-overlay-1.9.0.CR1.tar.gz. Распакоўваем зьмесьціва архіву ў <WILDFLY_HOME> і ў кансолі запускаем каманду.
    
    bin/jboss-cli.sh --file=bin/keycloak-install.cli
    
    Тым самым мы ўсталявалі KeyCloak у WildFly.

  2. Наступным крокам трэба ўсталяваць адаптар падтрымкі KeyCloak у сеціўных праграмах адсюль: http://keycloak.jboss.org/downloads.html?dir=0%3Dadapters/keycloak-oidc%3B. Для WildFly 8-ай вэрсіі файл keycloak-wf8-adapter-dist-1.9.0.CR1.tar.gz, а для 9-ай і 10-ай вэрсіяў файл keycloak-wildfly-adapter-dist-1.9.0.CR1.tar.gz. Ізноў распакоўваем зьмесьціва архіву ў <WILDFLY_HOME>, стартуем WildFly і ў кансолі запускаем каманду.
    
    bin/jboss-cli.sh --file=bin/adapter-install.cli
    
  3. Апошнім крокам трэба перазапусьціць WildFly і адкрыць старонку http://localhost:8080/auth. KeyCloak не прадстаўляе змоўчнага адміністратара, таму дадзеная старонка першай справай дасьць магчымасьць стварыць такога карыстальніка – увядзіце імя і пароль для яго. Калі ж вы ўсталёўваеце KeyCloak не на лякальнай машыне, тады стварыць першага адміністратара можна толькі пры дапамозе каманднага радка:
    
    bin/add-user.sh -r master -u <username> -p <password>
    

Наладкі KeyCloak

Каб злучыць сеціўную праграму, якую мы распрацоўваем, з сэрвэрам KeyCloak, трэба выканаць наступныя крокі на баку KeyCloak.

  1. Па-першае ствараем у KeyCloak вобласьць (realm), якая будзе ўтрымліваць у сабе ўсе наладкі бясьпекі, якія тычацца менавіта і толькі гэтай праграмы. Для гэтага наводзім курсор мышы на стрэлку ў левым верхнім куце:

    Зьявіцца акенца існуючых абласьцей і кнопка для стварэньня новай:

    Цісьнем на гэтую кнопку і ствараем новую вобласьць:

  2. Наступным крокам ствараем ролю для нашай праграмы. Для гэтага знаходзячыся ў створанай намі вобласьці (далейшыя крокі будуць адбывацца ў межах гэтай вобласьці), цісьнем на пункт мэню Roles у разьдзеле Configure:

    На старонцы, якая адкрылася, цісьнем на кнопку Add role:

    Уводзім назву ролі і цісьнем на кнопку Save:

  3. Далей ствараем новага карыстальніка. Цісьнем на пункт мэню Users у разьдзеле Manage:

    А потым на старонцы, якая адкрылася, цісьнем на кнопку Add user:

    Уводзім даныя карыстальніка і цісьнем на кнопку Save:

    Адкрываем укладку Credentials, уводзім пароль і пацьверджаньне паролю і цісьнем кнопку Reset Password (ня вельмі ўдалая назва):

    У выніку адкрыецца акенца с запытам пацьвердзіць зьмену паролю, цісьнем кнопку Change Password:

    Апошнім крокам злучаем карыстальніка з роляй, якую мы стварылі на папярэднім кроку. Адкрываем укладку Role Mappings, абіраем створаную раней ролю ў сэкцыі Available Roles і цісьнем кнопку Add Selected:

    Зьмены захаваюцца аўтаматычна.

  4. Наступным крокам вызначаем нашу праграму ў якасьці кліента KeyCloak. Цісьнем на пункт мэню Clients у разьдзеле Configure:

    А потым на старонцы, якая адкрылася, цісьнем на кнопку Create:

    Уводзім атрыбуты нашага кліента (ID, URL для вяртаньня пасьля аўтэнтыфікацыі і тып доступу) і цісьнем на кнопку Save:

Наладкі праграмы

Наладкі праграмы трохі адрозьніваюцца ў залежнасьці ад тэхналёгій, якія ў ёй выкарыстоўваюцца.

  1. Па-першае, калі гэта Java-кліент, трэба дадаць залежнасьці ў maven-канфігурацыю:
  2. 
    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-core</artifactId>
        <version>${version.keycloak}</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-adapter-core</artifactId>
        <version>${version.keycloak}</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-services</artifactId>
        <version>${version.keycloak}</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-jboss-adapter-core</artifactId>
        <version>${version.keycloak}</version>
        <scope>provided</scope>
    </dependency>
    
  3. Наступным крокам вяртаемся на UI KeyCloak, цісьнем на пункт мэню Clients у разьдзеле Configure, у падмэню выбіраем Installation і на старонцы, якая адкрыецца, у полі Format Option выбіраем пункт Keycloak OIDC JSON: Капіюем наладкі з поля Download, ствараем у нашым праекце ў тэчцы WEB_INF файл keycloak.json і ўстаўляем у гэты файл наладкі KeyCloak:
  4. Апошні крок, агульны для ўсіх тыпаў Java-кліентаў,– пазначыць тып аўтэнтыфікацыі ў файле web.xml:
  5. 
    <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    
        <!-- Declare to use KEYCLOAK authentication method -->
        <login-config>
            <auth-method>KEYCLOAK</auth-method>
            <realm-name>videomanager</realm-name>
        </login-config>
    </web-app>
    

Вышэйзгаданае будзе аднолькава для любых Java-кліентаў. А цяпер прывядзем наладкі, спэцыфічныя для розных тыпаў кліентаў.

  1. Калі гэта праграма на аснове сэрвлетаў, усё, што трэба зрабіць – гэта пазначыць абаронены сэрвлет (альбо яго асобныя мэтады) анатацыямі @DeclareRoles і @ServletSecurity:
    
    @WebServlet("/video-list-servlet")
    @DeclareRoles("video-app-user")
    @ServletSecurity(@HttpConstraint(rolesAllowed = {"video-app-user"}))
    public class VideoListServlet extends HttpServlet {
    }
    
    У гэтым выпадку пры спробе доступу да рэсурсу /video-list-servlet праграма вызначыць, што няма аўтэнтыфікаванай сэсіі і перанакіруе запыт да KeyCloak, адчыніцца яго старонка лагіну, пасьля ўводу лагін-інфармацыі, запыт ізноў будзе накіраваны на зыходны рэсурс /video-list-servlet. Гэтым разам будзе даступна аўтэнтыфікаваная сэсія, праграма запытае (ці атрымае гэта непасрэдна з JWT-токэну) ролю аўтэнтыфікаванага карыстальніка і, калі яго роля супадае з аб'яўленай (video-app-user), праграма задаволіць доступ да рэсурсу, інакш запыт будзе адхілены.
  2. Калі ж праграма зьяўляецца REST-сэрвісам, альбо зроблена на аснове JSF, патрэбна дадатковая наладка на ўзроўні web.xml:
    
    <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    
        <security-constraint>
            <web-resource-collection>
                <url-pattern>/*</url-pattern>
            </web-resource-collection>
            <auth-constraint>
                <role-name>video-app-user</role-name>
            </auth-constraint>
        </security-constraint>
    
        <login-config>
            <auth-method>KEYCLOAK</auth-method>
            <realm-name>videomanager</realm-name>
        </login-config>
    
        <security-role>
            <role-name>video-app-user</role-name>
        </security-role>
    </web-app>
    

Concurrency

Асобны модуль у дадатак да java.util.concurrent.

Spring

Учимся готовить: Spring 3 MVC + Spring Security + Hibernate на habrahabr.ru

Spring Core Tutorial на mkyong.com

Spring MVC Tutorial на mkyong.com

Spring Tutorial на tutorialspoint.com

Spring 4 Tutorial websystique.com

Spring MVC Tricks and Tutorials crunchify.com

Security

Spring Security Guides

Spring Security Tutorial на mkyong.com

Boot

Патрабаваньні для вэрсіі 1.2.3.RELEASE:

  • Java 7+
  • Servlet 3.1+
  • Spring Framework 4.1.5+
  • Maven 3.2+
  • Jetty 9+

Тыповая наладка ў pom.xml:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>myproject</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <!-- Inherit defaults from Spring Boot -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.3.RELEASE</version>
    </parent>

    <!-- Add typical dependencies for a web application -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <!-- Package as an executable jar -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Усталёўка CLI

Спампоўваем архіў і распакоўваем яго ў адвольную тэчку, напрыклад у /opt/spring-boot.

Ладкуем асяродак, дадаўшы ў ~/.profile:


SPRING_HOME=/opt/spring-boot
export SPRING_HOME

PATH=$PATH:$SPRING_HOME/bin
export PATH

Таксама ствараем сім-лінк :


ln -s /opt/spring-boot/shell-completion/bash/spring /etc/bash_completion.d/spring

View templates

Thymeleaf

Базы даных

Hibernate

Java Hibernate. Часть 1 — Введение

Java Hibernate. Часть 2 — Запросы

Java Hibernate. Часть 3 — Отношения

Java Hibernate. Часть 4 — Spring

tutorialspoint.com

mkyong.com

Міграцыі

Liquibase, Changelog samples

Running DB migration for a Java app

Java EE 7 Database Migrations with Liquibase and WildFly

Зборка

Ant

Java Ant. Часть 1 — Введение

Maven

Кніга «Better Builds With Maven»

Кніга «Maven by Example»

Кніга «Maven: The Complete Reference»

Усталёўка

Перш за ўсё трэба каб на кампутары быў ужо ўсталяваны JDK.


$ echo $JAVA_HOME
/usr/lib/jvm/java-7-openjdk-i386

Пасьля гэтага спампоўваем дыстрыбутыў і распакоўваем яго напрыклад сюды: /usr/local/apache-maven. Пры распакоўцы будзе створана тэчка з пазнакай вэрсіі, напрыклад: apache-maven-3.0.5.

Дадаем у асяродак зьменную M2_HOME:


$ export M2_HOME=/usr/local/apache-maven/apache-maven-3.0.5

Таксама дадаем шлях да bin-тэчкі мавена ў PATH


$ export PATH=$PATH:/usr/local/apache-maven/apache-maven-3.0.5/bin

Пры неабходнасьці дадаем зьменную асяродка MAVEN_OPTS, якая вызначае ява-парамэтры, зь якімі будзе запускацца мавен:.


$ export MAVEN_OPTS="-Xms256m -Xmx512m"

Усё, мавен усталяваны і гатовы для працы. Пераканацца ў гэтым можна так:


$ mvn --version
Apache Maven 3.0.5 (r01de14724cdef164cd33c7c8c2fe155faf9602da; 2013-02-19 16:51:28+0300)
Maven home: /usr/local/apache-maven/apache-maven-3.0.5
Java version: 1.7.0_51, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-7-openjdk-i386/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.8.0-19-generic", arch: "i386", family: "unix"

Наладкі

У агульным выпадку пасьля ўсталёўкі мавен цалкам гатовы для працы, але бываюць выпадкі, калі патрэбныя дадатковыя наладкі. Адным з такіх выпадкаў зьяўляецца наяўнасьць проксі-сэрвэра паміж працоўным кампутарам і інтэрнэтам.

Дадатковыя наладкі мавена зьмяшчаюцца ў файле settings.xml, які ў сваю чаргу можа зьмяшчацца ў двух розных месцах:

  • Тэчка мавена: $M2_HOME/conf/settings.xml
  • Тэчка карыстальніка: ${user.home}/.m2/settings.xml

Першы зь іх завецца файлам глябальнай канфігурацыі, другі – канфігурацыяй карыстальніка. Калі існуюць абодва гэтыя файлы, тады выніковая канфігурацыя складаецца ў выніку іх зьліцьця, пры гэтым канфігурацыя карыстальніка мае перавагу па-над глябальнай.

Прыклад наладкі проксі-сэрвэру:


<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <proxies>
    <proxy>
      <id>proxy</id>
      <active>true</active>
      <protocol>http</protocol>
      <host>proxy.compony.com</host>
      <port>8080</port>
    </proxy>
  </proxies>
  
</settings>

Стварэньне праекту

Стварыць новы праект можна наступным чынам (цяперашняй тэчкай павінна быць тэчка, у якой будуць зьмяшчацца вашы праекты):


$ mvn archetype:generate -DgroupId=com.company.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Гэтая каманда створыць тэчку my-app па значэньню парамэтра artifactId, а ў гэтай тэчцы наступную структуру:

my-app
|-- pom.xml
`-- src
    |-- main
    |   `-- java
    |       `-- com
    |           `-- mycompany
    |               `-- app
    |                   `-- App.java
    `-- test
        `-- java
            `-- com
                `-- mycompany
                    `-- app
                        `-- AppTest.java          
        

Канвэнцыя па-над канфігурацыяй

Maven прытрымліваецца ідэалёгіі канвэнцыя па-над канфігурацыяй, якая азначае, што распрацоўшчыку ня трэба самому выдумляць нейкія базавыя рэчы, а Maven сам іх перадвызначае, напрыклад месцазнаходжаньне зыходнікаў, рэсурсаў, тэстаў, скампіляваных файлаў і jar-файлаў перадвызначана наступным чынам:

Элемэнт Перадвызначанае месцазнаходжаньне
Зыходныя файлы праекту ${basedir}/src/main/java
Рэсурсы ${basedir}/src/main/resources
Тэсты ${basedir}/src/test
Скампіляваныя файлы ${basedir}/target/classes
jar-файлы ${basedir}/target

POM

Дэталі праекту, залежнасьцямі і зборкай якога кіруе Maven, знаходзяцца ў файле pom.xml, які павінен знаходзіцца ў корані праекту. Яго тыпічны зьмест наступны:


<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <!-- вэрсія мадэлі для POM-аў Maven 2.x заўсёды 4.0.0 -->
  <modelVersion>4.0.0</modelVersion>
  
  <!-- даныя для ідэнтыфікацыі праекту, то бок набор значэньняў,
     які дазваляе адназначна яго ідэнтыфікаваць -->
  <groupId>com.companyname</groupId>
  <artifactId>MavenExample</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <!-- залежнасьці ад бібліятэкаў -->
  <dependencies>
  
    <dependency>
        <!-- даныя для ідэнтыфікацыі бібліятэкі -->
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.10</version>

        <!-- на якім этапе зборкі будзе выкарыстоўвацца, 
            у дадзеным выпадку - выключна для запуска і кампіляцыі тэстаў -->
        <scope>test</scope> 
    </dependency>
  </dependencies>
  
</project>

Этапы

Запусьціць зборку праекта мавенам можна пры дапамозе кансольнай каманды mvn <ЭТАП> з месца, дзе знаходзіцца файл pom.xml (то бок знаходзячыся ў корані праекта). ЭТАП – гэта імя этапу жыцьцёвага цыкла зборкі праекта:

validate правярае карэктнасьць мэта-інфармацыі аб праекце
compile кампіліруе зыходнікі
test праганяе юніт-тэсты скампіляваных клясаў, якія атрымаліся на папярэднім этапе, выкарыстоўваючы падыходзячы тэставы фрэймворк
package пакуе праект у лёгкаперамяшчаемы фармат (JAR альбо WAR)
integration-test адпраўляе запакаваны праект у асяродак інтэграцыйнага тэставаньня і праганяе інтэграцыйныя тэсты
verify правярае упакаваны праект на карэктнасьць і задавальненьне крытэрам якасьці
install зьмяшчае пакет у лякальны рэпазыторый мавена, адкуль ён будзе даступны іншым праектам у якасьці залежнасьці
deploy зьмяшчае пакет на сэрвэр для рэальнай працы

Пры гэтым этапы асноўнага жыцьцёвага цыкла зборкі зьяўляюцца пасьлядоўнымі, і калі напрыклад запусьціць каманду mvn package, то будуць пасьлядоўна выкананы этапы validate, compile, test і напрыканцы сам package.

Акрамя гэтага у мавене ёсьць самастойныя этапы, якія існуюць па-за межамі звычайнага жыцьцёвага цыкла мавена і іх выкананьне не прыводзіць да аўтаматычнага выкананьня іншых этапаў, гэта:

clean выдаляе вытворныя артэфакты, якія былі створаныя мавенам раней
site стварае дакумэнтацыю для праекта

Профілі зборкі

Рэпазыторыі

Рэпазыторый мавена – гэта пляцоўка, дзе захоўваюцца джаркі праектаў, залежнасьці, плагіны і іншыя артэфакты, якія патрэбныя мавену для зборкі. Рэпазыторыі бываюць:

  • лякальны – гэта тэчка на лякальным кампутары, па змоўчваньні $HOME/.m2/repository, але можа быць перавызначана ў settings.xml:
  • 
    <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
    
      <localRepository>/my/special/path/to/repo</localRepository>
      
    </settings>
    
  • цэнтральны – знаходзіцца па адрасе http://repo1.maven.org/maven2/.
  • адлеглыя – уласныя рэпазыторыі распрацоўшчыкаў. Гэтыя рэпазыторыі можна пазначаць непасрэдна ў pom.xml:
  • 
    <project xmlns="http://maven.apache.org/POM/4.0.0"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
       <modelVersion>4.0.0</modelVersion>
       <groupId>com.companyname.projectgroup</groupId>
       <artifactId>project</artifactId>
       <version>1.0</version>
       <dependencies>
          <dependency>
             <groupId>com.companyname.common-lib</groupId>
             <artifactId>common-lib</artifactId>
             <version>1.0.0</version>
          </dependency>
       <dependencies>
       <repositories>
          <repository>
             <id>companyname.lib1</id>
             <url>http://download.companyname.org/maven2/lib1</url>
          </repository>
          <repository>
             <id>companyname.lib2</id>
             <url>http://download.companyname.org/maven2/lib2</url>
          </repository>
       </repositories>
    </project>
    

Калі мавену ў працэсе зборкі спатрэбіўся нейкі артэфакт, ён:

  1. спачатку пашукае яго ў лякальным рэпазыторыі, калі ён там ёсьць, возьме яго адтуль, інакш пяройдзе на кроку 2;
  2. пашукае яго ў цэнтральным рэпазыторыі (патрэбны доступ у інтэрнэт), калі ён там ёсьць, загрузіць яго ў лякальны рэпазыторый і возьме адтуль, інакш пяройдзе да кроку 3;
  3. калі пазначаная крыніца адлеглага рэпазыторыю, пашукае там, калі артэфакт там ёсьць, загрузіць яго ў лякальны рэпазыторый і возьме адтуль, інакш, калі патрэбнага артэфакту няма і ў адлеглым рэпазыторыі, альбо калі адлеглыя рэпазыторыі ўвогуле не пазначаныя, тады мавен спыніць працу з пазнакай памылкі.

Рэдка, але бываюць сытуацыі, калі пэўнага артэфакту няма ні ў якім з вядомых рэпазыторыяў, але ён даступны для загрузкі. У гэтым выпадку можна яго загрузіць на лякальны кампутар і дадаць у лякальны рэпазыторый наступнай камандай:


mvn install:install-file -Dfile= -DgroupId= -DartifactId= -Dversion= -Dpackaging=

Напрыклад, jdbc-драйвэр для работы з СКБД Oracle вэрсіі 10.2.0.1, можна зарэгістраваць наступнай камандай:


mvn install:install-file \
  -Dfile=ojdbc14.jar \
  -DgroupId=com.oracle \
  -DartifactId=oracle \
  -Dversion=10.2.0.1 \
  -Dpackaging=jar \
  -DgeneratePom=true

Плагіны

Па сутнасьці сваёй мавен – гэта фрэймворк выкананьня плагінаў. І каб зьмяніць нешта ў працэсе зборкі, якую ён робіць, усё што трэба зрабіць – гэта дадаць нейкі плагін, альбо наладзіць ужо існуючы. Напрыклад, каб наладзіць ява-кампілятар такім чынам, каб былі дазволеныя зыходнікі вэсріі 5.0, трэба зрабіць наступнае:


...
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>2.5.1</version>
      <configuration>
        <source>1.5</source>
        <target>1.5</target>
      </configuration>
    </plugin>
  </plugins>
</build>
...

Кожны плагін мае мэты (goals) – гэта пэўныя атамарныя апэрацыі, і ўсё, што робіць мавен (у межах этапаў зборкі), ён робіць выконваючы пэўныя мэты розных плагінаў. Калі нейкая мэта плагіну не злучана ні зь якім этапам зборкі, яе можна выканаць непасрэдна праз плагін, а не праз этап зборкі:


$ mvn :

Jetty

Каб Jetty правяраў зьмяненьні ў праграме і аўтаматычна падгружаў іх, трэба зьмяніць змоўчнае значэньне парамэтру scanIntervalSeconds з 0 напрыклад на 1:


...
<build>
  <plugins>
    <plugin>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-maven-plugin</artifactId>
      <version>9.2.3.v20140905</version>
      <configuration>
          <scanIntervalSeconds>1</scanIntervalSeconds>
      </configuration>
    </plugin>
  </plugins>
</build>
...

Для таго, каб аўтаматычная падгрузка працавала як чакаецца, Jetty абавязкова павінен запускацца праз каманду mvn jetty:run (ні ў якім разе не mvn jetty:run-exploded; апошняя будзе прыводзіць да таго, што праграма будзе стартаваць з сабранага war-файла, і адпаведна будзе губляцца ўвесь сэнс хуткага фідбэку на зьмяненьні).

Залежнасьці

У сэкцыі dependencies файла pom.xml пералічваюцца ўсе зьнешнія залежнасьці, якія патрэбныя для таго, каб праект змог быць сабраным:


<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Maven Quick Start Archetype</name>
  <url>http://maven.apache.org</url>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.12</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
</project>

Пры гэтым у элемэнце scope пазначаецца вобласьць (scope), для якой гэтая залежнасьць мае дачыненьне, а таксама ўплывае на classpath, які ўжываецца для розных задач мавена. Магчымыя вобласьці:

  • compile – гэта змоўчная вобласьць, яна ўжываецца ў тым ліку тады, калі яўна не была пазначаная. Залежнасьці пазначаныя такім чынам, даступныя ўва ўсіх classpath праекту, і больш таго, даступныя ў залежных праектах.
  • provided – падобна да compile, але чакаецца, што JDK, альбо кантэйнэр самі прадставяць залежнасьці ў runtime. Напрыклад, гэта тычыцца Servlet API, які павінен прадставіць вэб-кантэйнэр.
  • runtime – пазначае, што залежнасьць патрэбная не для кампіляцыі, але для выкананьня праекту. Будзе даданая ў тэставы і runtime classpath.
  • test – пазначае, што залежнасьць не патрэбная для зборкі і працы праграмы, але толькі для кампіляцыі і выкананьня тэстаў.
  • system – падобна да provided, але адрозьніваецца тым, што шлях да jar-файлу залежнасьці павінен быць яўна пазначаны.
  • import (maven 2.0.9+) – ужываецца толькі для залежнасьцяў з тыпам pom у сэкцыі <dependencyManagement>

Common Annotations API


<!-- 'provided' scope in case of JBoss WildFly as the API is included there -->
<dependency>
  <groupId>org.jboss.spec.javax.annotation</groupId>
  <artifactId>jboss-annotations-api_1.2_spec</artifactId>
  <scope>provided</scope>
</dependency>

Web


<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

</web-app>

JavaEE CDI API


<!-- 'provided' scope in case of JBoss WildFly as the API is included there -->
<dependency>
  <groupId>javax.enterprise</groupId>
  <artifactId>cdi-api</artifactId>
  <scope>provided</scope>
</dependency>

JavaServer Faces


<!-- 'provided' scope in case of JBoss WildFly as the API is included there -->
<dependency>
  <groupId>org.jboss.spec.javax.faces</groupId>
  <artifactId>jboss-jsf-api_2.2_spec</artifactId>
  <scope>provided</scope>
</dependency>

Альбо больш vendor independent:


<dependency>
  <groupId>javax.faces</groupId>
  <artifactId>javax.faces-api</artifactId>
  <version>2.2</version>
</dependency>

EJB


<!-- 'provided' scope in case of JBoss WildFly as the API is included there -->
<dependency>
  <groupId>org.jboss.spec.javax.ejb</groupId>
  <artifactId>jboss-ejb-api_3.2_spec</artifactId>
  <scope>provided</scope>
</dependency>

JPA API


<!-- 'provided' scope in case of JBoss WildFly as the API is included there -->
<dependency>
  <groupId>org.hibernate.javax.persistence</groupId>
  <artifactId>hibernate-jpa-2.1-api</artifactId>
  <scope>provided</scope>
</dependency>

<!-- Annotation processor to generate the JPA 2.0 metamodel classes for 
    typesafe criteria queries -->
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-jpamodelgen</artifactId>
  <scope>provided</scope>
</dependency>

<!-- Annotation processor that raising compilation errors whenever constraint 
    annotations are incorrectly used. -->
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator-annotation-processor</artifactId>
  <scope>provided</scope>
</dependency>

Bean Validation


<!-- 'provided' scope in case of JBoss WildFly as the API is included there -->
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator</artifactId>
  <scope>provided</scope>
  <exclusions> <!-- Гэта патрэбна, альбо толькі для гэтага пэўнага выпадку? -->
    <exclusion>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
    </exclusion>
  </exclusions>
</dependency>

JAX-RS API


<!-- 'provided' scope in case of JBoss WildFly as the API is included there -->
<dependency>
  <groupId>org.jboss.resteasy</groupId>
  <artifactId>jaxrs-api</artifactId>
  <scope>provided</scope>
</dependency>

Лагіраваньне


<!-- 'provided' scope in case of JBoss WildFly as the API is included there -->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.7</version>
  <scope>provided</scope>
</dependency>

Testing


<!-- Needed for running tests (you may also use TestNG) -->
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <scope>test</scope>
</dependency>

<!-- Optional, but highly recommended -->
<!-- Arquillian allows you to test enterprise code such as EJBs and Transactional(JTA) 
     JPA from JUnit/TestNG -->
<dependency>
  <groupId>org.jboss.arquillian.junit</groupId>
  <artifactId>arquillian-junit-container</artifactId>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>org.jboss.arquillian.protocol</groupId>
  <artifactId>arquillian-protocol-servlet</artifactId>
  <scope>test</scope>
</dependency>

Gradle

Лагіраваньне

Трэба дадаць залежнасьці ў pom.xml:


...
<dependencies>
  ...
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.10</version>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.10</version>
  </dependency>
  ...
</dependencies>
...

Дадаць наладкі Log4J у файл log4j.properties па шляху src/main/resources:


#---  comment/uncomment needed lines to use for loging  -----------#
log4j.rootCategory=DEBUG, file
#------------------------------------------------------------------#

##### Appenders area #####
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=logs/app.log
log4j.appender.file.MaxFileSize=2MB
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%5p[%d{mm:ss}]%M(%F:%L) %m%n

Тэставаньне

Аўтаматызацыя

Gauge

Getting Started with Java and IntelliJ IDEA

Gauge example in Java

Cucumber

Unit-тэставаньне

Mockito JavaDoc

The JMockit Testing Toolkit Tutorial

JMockit samples

surefire – maven-убудаваньне для праверкі ступені пакрыцьця тэстамі.

Функцыянальнае тэставаньне

Интеграционное тестирование в Java EE

Java EE integration testing with Arquillian using Chameleon, Shrinkwrap, Drone/Graphene

UI-тэставаньне

Selenium Automation Testing Tutorial

Мікрасэрвісы

Deploying Java EE Microservices on OpenShift

Building Microservices with WildFly Swarm and Netflix OSS on OpenShift

Examples of how to write applications using WildFly Swarm


async-io: Atmosphere framework

akka.io: Build powerful concurrent and distributed applications more easily

kumuluzEE: A lightweight framework for packing, bootstrapping and deploying Java EE components as microservices.

QBit is a reactive programming lib for building microservices - JSON, HTTP, WebSocket, and REST. QBit uses reactive programming to build elastic REST, and WebSockets based cloud friendly, web services. SOA evolved for mobile and cloud. ServiceDiscovery, Health, reactive StatService, events, Java idiomatic reactive prog…

Microservice Service Discovery with Consul

Consul Service Discovery and Health For Microservices Architecture Tutorial

Eureka is a REST based service that is primarily used in the AWS cloud for locating services for the purpose of load balancing and failover of middle-tier servers.

Карысныя бібліятэкі

Guava: Google Core Libraries for Java

PrettyTime: intuitive Java date and timestamp formatting

PrettyFaces: URL-rewriting library with support for JSF

Rewrite: Routing and URL rewriting solution for JavaEE

HowTo's

Guide to Regular Expressions in Java: Part 1, Part 2

Аналізатары кода

SonarQube

Змоўчны адміністратар: admin/admin.

Старт сэрвэра: sudo bash /opt/sonarqube-5.0.1/bin/linux-x86-32/sonar.sh start

Servers

Jetty

WildFly

Наладка SSL

SSL setup guide

how to configure ssl in wildfly 8.2.0 server?

Configuring SSL in Wildfly 8/9/10

Усталёўка драйвэра PostgreSQL

  1. спампоўваем адпаведную вэрсію драйвэру з https://jdbc.postgresql.org/download.html у тэчку /tmp
  2. Запускаем кансоль WildFly:
    
    bin/jboss-cli.sh
    
    і ў кансолі злучаемся з сэрвэрам:
    
    You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
    [disconnected /] connect
    [standalone@localhost:9990 /] 
    
  3. Наступным крокам запускаем каманду ў кансолі:
    
    module add --name=org.postgres --resources=/tmp/postgresql-9.4-1202.jdbc42.jar --dependencies=javax.api,javax.transaction.api,javax.servlet.api
    
  4. А потым такую:
    
    /subsystem=datasources/jdbc-driver=postgres:add(driver-name="postgres",driver-module-name="org.postgres",driver-class-name=org.postgresql.Driver)
    

Цяпер у файле канфігурацыі WildFly (standalone.xml) можна дадаваць datasource да БД PostgreSQL:


<subsystem xmlns="urn:jboss:domain:datasources:4.0">
    <datasources>
        <datasource jta="true" jndi-name="java:jboss/datasources/PostgresDS" 
              pool-name="PostgresDS" enabled="true" use-java-context="true" use-ccm="true">
            <connection-url>jdbc:postgresql://localhost:5432/example</connection-url>
            <driver>postgres</driver>
            <security>
                <user-name>user</user-name>
                <password>password</password>
            </security>
        </datasource>
        ...
        <drivers>
            ...
            <driver name="postgres" module="org.postgres">
                <driver-class>org.postgresql.Driver</driver-class>
            </driver>
        </drivers>
    </datasources>
</subsystem>

Воблачны хостынг

Heroku

Java on Heroku

Learn More about Heroku for Java

Deploying Tomcat-based Java Web Applications with Webapp Runner

Using WebSockets on Heroku with Java and the Play Framework

Connecting to Relational Databases on Heroku with Java

Даведнікі пераехалі на GitHub Pages. Актуальная вэрсія даступная па адрасе: https://yurtsevich.github.io/refs/java/