niedziela, 22 czerwca 2014

Wielowątkowość - Executor framework

Wykonywanie zadań bezpośrednio

 

Bezpośrednio tworzenie i wykonywanie zadań ma szereg wad:
  • tworzenie i kończenie pracy wątku nie jest darmową operacją i opóźnia wykonanie wątku, jeśli wątków jest wiele a zadanie do wykonania proste to uwidacznia się to jeszcze bardziej zwiększając drastycznie opóźnienie (latency)
  • każdy wątek ma swój stos - konsumuje pamięć którą jest przydzielona JVM
  • nieograniczenie tworzenie wątków może spowodować że systemowi zabraknie zasobów i przestanie działać
 

Executor serivce

 

Executor framework pozwala na oddzielenie tworzenia zadań i od ich uruchamiania. Do uruchamiania zadań służy np klasa ThreadPoolExecutor.

Aby stworzyć executora do wykonywania zadań można skorzystać z klasy Excutors i metody fabrykującej newFixedThreadPool dla executora ze stałą liczbą wątków lub newCachedThreadPool dla executora bez ograniczenia liczby wątków.

Executor nie tworzy dla każdego zadania nowego wątku - jeśli wątek zakończy pracę, bierze następne zadanie a do kolejkowania zadań służy kolekcja blokująca (BlockingQueue).

Używanie interfejsu ExecutorService pozwala nam w każdym momencie podmienić sposób wykonywania zadań, i ustalić wiele innych właściwości:
  • w którym wątku zadanie będzie wykonywane
  • w jakiej kolejności zadania będą wykonywane
  • ile zadań może działać naraz
  • jak wiele zadań może być kolejkowanych
  • specyfikacje czy task może być odrzucony z powodu przeciążenia i w jaki sposób ma to być zrobione
  • czy przed i po wykonaniu zadania wykonać inne zadanie
Wszystko to można dokonać przez użycie bezpośrednio konstruktorów np klasy ThreadPoolExecutor.

ExecutorService ma trzy stanu - running, shutting down, terminated. Metoda shutdown zaczyna bezpieczne zamykanie - wszystkie submitowane i działające taski są kończone a następnie executor jest zamykany. Możliwe jest też użycie metody shotdownNow które stara się anulować aktualnie działające zadania i nie startuje nowych.
Należy pamiętać że aby maszyna wirtualna się zamknęła nie mogą działać żadne jej wątki które nie są demonami więc należy zamknąć ExecutorService aby skończyć jego wątki. Najlepiej zrobić to wykonując metodę shutdown a następnie awaitTermination z odpowiednio dużym czasem.

Inne rodzaje ExecutorService:
  • ForkJoinPool - do wykonywania specjalnych zadań ForkJoinTask  które umożliwiają łatwą implementacje problemów które można podzielić na mniejsze
  • ScheduledThreadPoolExecutor - do tworzenia zadań które będą dostępne (wykonywane) w określonym czasie
 

CompletionService


Pakiet java.util.concurent zawiera także interfejs CompletionService i jego klasę implementującą ExecutorCompletionService. Dzieki tej klasie można w łatwy sposób pobrać wyniki wykonanych zadań jeśli są one potrzebne - nie trzeba pobierać Future dla każdego zadania i iterować po tym. Dzięki temu dostaniemy pierwszy wynik zaraz po jego zakończeniu bo wyniki zapisywane są na kolejce blokującej.
ExecutionCompletionService potrzebuje executora aby działać - więc można tworzyć wiele takich serwisów dla jednego executora, można także przekazać implementację kolejki blokującej której chcemy użyć dla skończonych zadań.

sobota, 21 czerwca 2014

Wielowątkowość - kolekcje synchronizowane i wielowątkowe, synchronizatory

Kolekcje synchronizowane


Java zawiera kolekcje synchronizowane - stare kolekcje Vector i Hashtable. Można także z każdej niesynchronizowanej kolekcji zrobić taką poprzez metodę fabrykującą Collections.synchronizedXXX np synchronizedMap. Fabryka taka tworzy obiekt który dekoruje każdą publiczną metodę opakowując ją blokiem synchronized używając instancji obiektu do blokowania.

Kolekcje synchronizowane nie mają takich metod jak putIfAbsent, ich iteratory są iteratorami fail-fast - czyli jeśli wykryją modyfikacje w trakcie iterowania wyrzucają wykątek ConcurrentModificationException. Blokowanie iteratora na czas iterowania może być bardzo kosztowne i sprawiać że program nie będzie skalowalny.


Kolekcje wielowątkowe (concurrent collections)

 

W javie 5 wprowadzono kolekcje wielowątkowe takie jak ConcurrentHashMap, CopyOnWriteArrayList, CopyOnWriteArraySet, ArrayBlockingQueue, LinkedBlockingQueue, LinkedBlockingDequeue

ConcurrentHashMap nie synchronizuje wszystkich metod zamiast tego wykorzystywany jest tu mechanizm podziału na wiele map każda ze swoją blokadą - lock striping dzięki czemu więcej wątków może symultanicznie wykonywać operacje na obiekcie mapy. Dodatkowo mapa implementuje interfejs ConcurentMap w którym są dodatkowe atomowe metody: putIfAbsent, remove, replace. Iterator takiej mapy nie wyrzuca wyjątku jeśli mapa jest modyfikowana jednak jest iteratorem weakly consistent - czyli nie musi pokazywać zmian po utworzeniu iteratora.

CopyOnWriteArrayList jest klasą thread-safe i zastępuje ArrayList - tworzy ona kopie podległej wewnętrznej tablicy za każdym razem kiedy wykonywana jest operacja dodawania, lub usuwania obiektu - dzięki temu iterator po stworzeniu może iterować na tablicy na której został zainicjowany więc nie wyrzuci wyjątku ConcurrentModificationException. Klasa taka ma zastosowanie tam gdzie dominującą operacją jest iterowanie a nie dodawanie np. listenery.

Kolejki blokujące mogą być użyte do rozwiązania problemu producent-konsumer. Zawierają metody blokujące które czekają jeśli kolejka jest pusta lub pełna.

Kolejki dequeue umożliwają pobieranie elementów także z początku kolejki dzięki czemu mogą być używane w alborytmach work-stealing w których każdy konsument ma jedną kolejkę i jeśli nie ma więcej pracy może wziąć pracę z początku innego konsumenta - dzięki temu wątki mniej będą ze sobą walczyły (less contention) i dzięki temu rozwiązanie jest bardziej skalowalne.


Metody blokujące które rzucają wyjątek IterruptedException


Każdy wątek może zostać zablokowany lub zapauzowany z różnych powodów - oczekiwaniu na blokadzie, czekanie na skończenie operacji I/O, czekanie na wybudzenie z operacji sleep. W takim wypadku proces przechodzi w stan BLOCKED, WAITING, TIMED_WAITING. Wątek który jest zablokowany musi być czekać na operacje na którą nie ma wpływu a która go wybudzi.

Nie da się zatrzymać wątku z innego wątku - można mu tylko ustawić flagę interrupt poprzez metodę interrupt. Mechanizm ten jest w większości wykorzystywane to anulowania długo trwających operacji.

Jak obsłużyć InterruptedException:
  • poprzez propagowanie go wyżej do metody wywołującej
  • poprzez obsługę - jeśli nie można go wyżej propagować - poprzez restorowanie flagi interrupted: Thread.currentThread().interrupt()
Można też pominąć obsługę jeśli pracujemy bezpośrednio na klasie Thread.

Synchronizatory

 

Semaphore - klasa przydatna np przy pulach obiektów. 

CountDownLatch - jednorazowy zatrzask który odlicza się on n do zera, wątki czekają na to aż wartość będzie równa 0. Może być użyty tylko raz.

CyclicBarrier - tak jak zatrzask blokują grupę wątków aż pewna aktywność się skończy - znana - stała liczba wątków dojdzie do bariery, następnie bariera jest resetowana a wątki mogą kontynuwać działanie. Jeśli wątek na barierze zostanie przerwany lub wystąpi timeout to reszta wątków zostanie wybudzona przez BrokenBarrierException a bariera zostanie w stanie broken. 

Phaser - jest bardziej wyrafinowaną barierą, umożliwia rejestracje i wyrejestrowywanie wątków oczekujących.

Exchanger pozwala na wymianę obiektu między wątkami w miejscu "spotkania".

 

 

piątek, 20 czerwca 2014

Wielowątkowość - współdzielenie obiektów

Widzialność

 

Jeśli nie zastosowana jest synchronizacja kompilator może zmienić kolejność wykonywania poleceń więc jeśli dwie zmienne są przypisywane jedna po drugiej - nie ma pewności że ich wartości będą widoczne w tym samym czasie dla innego wątku w tej samej kolejności. Możliwe jest widzenie starych wartości (stale data) a w przypadku zmiennych 64bitowych możliwe jest widzenie połowy wartości z dwóch wartości którą ta zmienna miała ponieważ ich zamiana jest robiona w dwóch krokach.

Jednym z rozwiązań jest stosowanie blokad - jeśli wątek uzyska blokadę następnie zmienni zmienną i zwolni blokadę, to inny wątek który uzyskał tą samą blokadę będzie widzieć wszystkie dane.

Innym przykładem są zmienne volatile. Jeśli jeden wątek A zapisuje do zmiennej volatile wartość a następnie inny wątek B ją czyta to zobaczy dobrą wartość zmiennej jak i innych wcześniej zmienianych zmiennych przez wątek A.

Przykład: Wątek Reader nie widzi zmiennej ready więc będzie działał w nieskończoność

public class NoVolatile {
    private static boolean ready = false;
       private static class Reader extends Thread {
        public void run() {
            int i = 0;
            while(!ready) {
                i++;
            }
        }
    }
     public static void main(String[] args) throws InterruptedException {
        new Reader().start();
        TimeUnit.SECONDS.sleep(1);
        ready = true;
    }
}


Po zastosowaniu słowa kluczowego volatile program się zakończy po sekundzie jak przewidywano:

public class VolatileExample {
    private static volatile boolean ready = false;
    private static class Reader extends Thread {
        public void run() {
            int i = 0;
            while(!ready) {
                i++;
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        new Reader().start();
        TimeUnit.SECONDS.sleep(1);
        ready = true;
    }
}


Powiązanie z wątkiem - thread confinement

 

Jeśli dane (klasa) nie jest dostępna dla wielu wątków wtedy nie trzeba stosować synchronizacji ani tworzyć klas thread-safe. Przykładem jest Swing gdzie jest jeden wątek odpowiedzialny za obsługę eventów. Mechanizm ten jest mechanizmem który może być jedynie zaznaczony w dokumentacji - nie da się wymusić aby klasa była używana tylko w jednym wątku.

Przykłady powiązań z wątkiem:
  • zmienna volatile którą czyta wiele wątków ale zmienia tylko jeden
  • zmienna lokalna w metodzie - jest powiązana ze stosem wywołania
  • zmienna ThreadLocal - dla każdego wątku inna instancja

Niezmienność - Immutability

 

Klasy immutable są zawsze thread-safe. Mogą mieć tylko jeden stan który jest ustawiany przy tworzeniu klasy.
Klasa jest niezmienna (immutable) jeśli:
  • jej stan nie może być zmieniony po konstrukcji
  • wszystkie pola są final
  • jest poprawnie stworzona - jej referencja nie "ucieknie" podczas konstrukcji
Klasa (instancja) immutable  może być bezpiecznie publikowana nawet bez użycia synchronizacji! Wystarczy że została poprawnie stworzona.

Publikacja i ucieczka 

 

Publikacja to udostępnianie zmiennych na zewnątrz.
Ucieczka this jest możliwa np:

public class Escape {
    public Escape(EventSource eventSource) throws InterruptedException {
        eventSource.registerListener(new EventListener() {
            @Override
            public void onEvent() {
                someMethod();
            }
        });
    }
}


Aby bezpiecznie opublikować obiekt obie referencja obiektu jak i jego stan muszą być widoczne dla innych wątków  w tym samym czasie. Aby tego dokonać należy:
  • zainicjować obiekt ze statycznego inicjatora - inicjowanie przy deklarowaniu statycznego pola
  • trzymać jego zmienną w polu volatile albo AtomicReference
  • trzymać jego zmienną w polu final w poprawnie skonstruowanym obiekcie
  • trzymać jego zmienną w polu który jest poprawnie chronione przez blokadę ( tyczy się także kolekcji synchronizowanych albo innych bezpiecznych kolekcji jak ConcurentHashMap, Vector, BlockingQueue itp
Część obiektów można nazwać efektywnie niezmiennymi - czyli nie są one immutable ale po stworzeniu nie są zmieniane aby ich używać należy tylko je bezpiecznie publikować.
Jeśli chodzi o obiekty zmienne - muttable - to należy je bezpiecznie publikować jak i muszą być thread-safe albo muszą być chronione przez blokadę.
Jeśli chodzi o obiekty immutable - to nie muszą być one publikowane bezpiecznie - mogą być jakkolwiek - np przez pole public.


czwartek, 19 czerwca 2014

Wielowątkowość - Thread Safety

 Thread safety


Jeśli wiele wątków dostaje się/może się dostać do zmiennej stanu bez jakiejkolwiek synchronizacji to program nie będzie działał dobrze, aby tak się stało musi być zrobiona jedna z poniższych rzeczy:
  • zmienna stanu nie powinna być dostępna
  • zmienna stanu powinna być niezmienna (immutable)
  • powinno się użyć synchronizacji przy dostępie do zmiennej

Jak zdefiniować klasę która jest thread-safe?
Klasa taka działa dobrze kiedy jest dostępna/używana przez wiele wątków bez względu na przeplatanie się i harmonogram ich działania bez dodatkowej synchronizacji po stronie kodu wywołującego. Klasa taka zawiera wszystkie mechanizmy synchronizacji w sobie.

Klasy bez stanu są zawsze thread-safe.

Atomowość 

 

Problemem mogą być operacje złożone dla których mogą wystąpić race-conditions.
Dwa typy race-conditions:
  • check-then-act
  • read-modify-write
Przykłady race-conditions:
  • "singleton" class z niesynchronizowaną metodą getInstance()
  • niesynchronizowana operacja zwiększania licznika count++
Operacje A and B są wzajemnie do siebie atomowe jeśli z perspektywy wątku wykonującego A jeśli inny wątek wykonuje B to jest ona wykonana w całości albo w ogóle.

Blokowanie 

 

Klasa która ma jedną zmienną tworzącą stan może być thread safe jeśli zmienna jest atomowa (np AtomicLong), po dodaniu drugiej już tak nie musi być jeśli zmienne nie są niezależne. Aby zmiana zmiennych była atomowa należy zastosować blokowanie poprzez użycie bloków lub metod synchronized. Ten rodzaj blokad nazywa się intrinsic lock. Blokady te są wielo-wejściowe (reentrant) więc wątek może uzyskać dostęp do blokady ponownie jeśli już go ma.

Strzeżenie zmiennych blokadami


Aby złożone akcje (check-then-act, read-modify-write) były atomowe muszą się znaleźć w bloku synchronizowanym przez blokadę.

Dla każdej zmiennej do której może mieć dostęp wiele wątków wszystkie dostępy muszą być przeprowadzone z tą samą blokadą.

Dostęp do zmiennych powiązanych musi się odbywać na tej samej blokadzie.

Żywotność i wydajność

 

Należy zmniejszać bloki synchronizowane tylko na czas zmian zmiennych, nie należy w nich wykonywać długich operacji szczególnie tych związanych z siecią i dostępem do I/O.


sobota, 10 maja 2014

Proste połączenie spring, jpa (hibernate), spring data, postgresql

Proste zestawienie aplikacji z JPA i spring data.

1. Tworzymy projekt mavenowy w eclipsie z archetypu maven.archetype.webapp - aplikacja będzie aplikacją webową - za pomocą żądania restowego przetestujemy pobieranie danych z bazy.

2. Dodajemy potrzebne zależności do poma:
zależności mvc, rest: servlet-api, jackson-mapper-asl
mamy dodatkowe zależności do jpa: hibernate-entitymanager, jta, spring-jdbc, spring-orm, spring-data-jpa i postgresql.

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.java.ro</groupId>
    <artifactId>Invoices</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>Invoices Maven Webapp</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>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.4.1</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>4.3.4.Final</version>
        </dependency>
        <dependency>
            <groupId>javax.transaction</groupId>
            <artifactId>jta</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>3.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>3.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.3-1100-jdbc41</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.4.3.RELEASE</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-aop</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <build>
        <finalName>Invoices</finalName>
    </build>
</project>


3. Konfigurujemy web.xml: dispacher servler oraz listener - dispacher będzie korzystać z pliku kontekstu do mvc (resta) a listener wczyta plik kontekstu do jpa:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Invoices</display-name>
   
    <!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/root-context.xml</param-value>
    </context-param>
   
    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>


 4. Konfigurujemy serwis restowy (servlet-context.xml):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

    <mvc:annotation-driven/>
   
    <context:component-scan base-package="com.java.ro.invoices.controler"/>
   
    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="order" value="1"/>
    </bean>
</beans>


5. Konfigurujemy jpa (root-context.xml): do konfiguracji potrzeba 3 podstawowych cegiełek:
  •  entityManagerFactory
  • transactionManager
  • dataSource
 oraz kilku innych beanów:
  • PersistenceAnnotationBeanPostProcessor - aby umożliwić wstrzyiwanie EntityManagera
  • jpa:repositories - określenie bazowego pakietu dla repozytorium
  • tx:annotation-driven - określenie że tranzakcyjność będzie zarządzana anotacjami
 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xmlns:repository="http://www.springframework.org/schema/data/repository"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
        http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository-1.6.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">

    <!-- config with annotation -->
    <context:annotation-config />
   
    <tx:annotation-driven transaction-manager="transactionManager" />
   
    <context:component-scan base-package="com.java.ro.invoices"/>

    <!-- spring data repositories base package -->
    <jpa:repositories base-package="com.java.ro.invoices.model.repository" />

    <!-- use persistence context annotation -->
    <bean
        class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="invoicesPersistenceUnit" />
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
            </bean>
        </property>
        <property name="jpaPropertyMap">
            <map>
                <entry key="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
                <entry key="hibernate.hbm2ddl.auto" value="create" />
                <entry key="hibernate.format_sql" value="true" />
            </map>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.postgresql.Driver" />
        <property name="url" value="jdbc:postgresql://localhost:5432/faktury" />
        <property name="username" value="invoice" />
        <property name="password" value="123" />
    </bean>

</beans>


6. W entity manager factory został podany persitence unit, należy stworzyć więc persistence.xml z tym persistent unitem - należy go umieścić w katalogu META-INF (scr/main/resources/META-INF):

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
  version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
  <persistence-unit name="invoicesPersistenceUnit">
  </persistence-unit>
</persistence>


7. Stworzenie przykładowej encji:

package com.java.ro.invoices.model.entity;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Invoice implements Serializable {
   
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private Long id;
   
    @Column(length = 20, nullable = false)
    private String number;
   
    @Column(nullable = false)
    private Date dateOfIssue;
   
    @Column(nullable = false)
    private Date maturity;
   
    @Column(nullable = false, precision = 10, scale = 2)
    private BigDecimal totalAmount;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public Date getDateOfIssue() {
        return dateOfIssue;
    }

    public void setDateOfIssue(Date dateOfIssue) {
        this.dateOfIssue = dateOfIssue;
    }

    public Date getMaturity() {
        return maturity;
    }

    public void setMaturity(Date maturity) {
        this.maturity = maturity;
    }

    public BigDecimal getTotalAmount() {
        return totalAmount;
    }

    public void setTotalAmount(BigDecimal totalAmount) {
        this.totalAmount = totalAmount;
    }
}


8. Tworzymy repozytorium dla encji - repozytorium będzie dziedziczyć z CRUDRepository:

package com.java.ro.invoices.model.repository;

import java.util.List;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.java.ro.invoices.model.entity.Invoice;

@Repository
public interface InvoiceRepository extends CrudRepository<Invoice, Long> {

    List<Invoice> findAll();
}


9. Tworzymy prosty serwis - bardzo ważna jest adnotacja Transactional jeśli chcemy coś zapisywać:

package com.java.ro.invoices.model.service;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.java.ro.invoices.model.entity.Invoice;
import com.java.ro.invoices.model.repository.InvoiceRepository;

@Service
@Transactional
public class InvoiceService {
   
    @Autowired
    private InvoiceRepository repository;
   
    public List<Invoice> getInvoices() {
        return repository.findAll();
    }
}


10. Tworzymy kontroler mvc aby pobrać dane:

package com.java.ro.invoices.controler;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.java.ro.invoices.model.entity.Invoice;
import com.java.ro.invoices.model.service.InvoiceService;

@Controller
public class InvoiceControler {
   
   
    @Autowired
    private InvoiceService invoiceService;
   
    @RequestMapping(value="/getInvoices", method= RequestMethod.GET)
    public @ResponseBody List<Invoice> getInvoices() {
        return invoiceService.getInvoices();
    }
   
}


11. Instalujemy postgre sql i konfigurujemy bazę danych i użytkownika.
12. Testujemy aplikację startując ją na tomcacie: wchodzimy na stronę: http://localhost:8080/Invoices/getInvoices:

strona zwracja [] co jest zrozumiałe bo nie ma żadnych danych,
dodajemy dane ręcznie do bazy danych:

INSERT INTO invoice.invoice(
            id, dateofissue, maturity, "number", totalamount)
    VALUES (1, TIMESTAMP '2014-04-01 00:00:00', TIMESTAMP '2014-04-30 00:00:00', 'FV-2014-4', 122.32);

INSERT INTO invoice.invoice(
            id, dateofissue, maturity, "number", totalamount)
    VALUES (2, TIMESTAMP '2014-05-01 00:00:00', TIMESTAMP '2014-05-31 00:00:00', 'FV-2014-5', 445.76);


odświerzamy stronę i otrzymujemy rezultat:


Aplikacja zwraca rekordy z bazy danych.

piątek, 9 maja 2014

Najprostrzy serwis REST za pomocą spring mvc i tomcata

1. Tworzymy projekt mavenowy w eclipsie z archetypu maven.archetype.webapp

2. Dodajemy potrzebne zależności do poma:

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.java.ro</groupId>
    <artifactId>RestMvc</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>RestMvc Maven Webapp</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>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.4.1</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>RestMvc</finalName>
    </build>
</project>



3. Następnie konfigurujemy dispacher servlet w web.xml który będzie nam obsługiwał żądania i przekazywał dalej. Servlet potrzebuje contextu springa który podawany jest w parametrze.

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>RestMvc</display-name>
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>*.json</url-pattern>
    </servlet-mapping>
</web-app>


 4. Kolejną konfiguracyjną rzeczą jest stworzenie pliku kontekstu spring który jest podawany do dispatcher servletu (/WEB-INF/spring/servlet-context.xml):
 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

    <mvc:annotation-driven/>
  
    <context:component-scan base-package="com.java.ro.restmvc.controler"/>
  
    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="order" value="1"/>
    </bean>
</beans>


5. Teraz jesteśmy już gotowi do stworzenia kontrolera który obsłuży nam żądanie i zwróci dane w formacie json:

5.1 Potrzebujemy danych które zostaną zwrócone:

package com.java.ro.restmvc.model;

import java.util.Date;

public class Person {
   
    private String name;
    private String surname;
    private Date dateOfBirth;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public Date getDateOfBirth() {
        return dateOfBirth;
    }

    public void setDateOfBirth(Date dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
    }
}


5.2 Kontroler który obsłuży żądanie:

package com.java.ro.restmvc.controler;

import java.util.Date;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.java.ro.restmvc.model.Person;


@Controller
public class PersonControler {
   
    @RequestMapping(value="/getPerson", method= RequestMethod.GET)
    public @ResponseBody Person getPerson() {
        Person p = new Person();
        p.setDateOfBirth(new Date());
        p.setName("Jan");
        p.setSurname("Kowalski");
        return p;
    }
}


6. Teraz wystarczy podłączyć Tomcata do eclipsa, dodać projekt i uruchomić.
7. Aby dostać się do danych wystarczy w przeglądarce wpisać:

http://localhost:8080/RestMvc/getPerson.json

Oto wynik: