VÝVOJ PODNIKOVÝCH APLIKACÍ NA PLATFORMĚ JAVA - CVIČENÍ Zbyněk Šlajchrt http://java.vse.cz/4it447/HomePage Část 9.
Program Diskuse nad domácím úkolem Aplikace Fotoalbum Verzování entit Využití inteceptoru pro synchronizaci keše Využití REST služby pro ruční čištění keše
Úklid keše na základě verze Umožňuje úklid pouze obstarožních ikon Zavedeme verzi do entity Photo Nový atribut private int version; getter anotujeme @javax.persistence.Version JPA automaticky začne po každé modifikaci entity zvyšovat hodnotu v tomto poli Používáno také pro optimistické zamykání Pozn.: Pravděpodobně bude nutné odstranit tabulku PHOTO: DROP TABLE PHOTO
Úprava metody cleanCache Porovnává aktuální verze fotek s verzemi ikon v keši Načte z DB entity všech fotek, které jsou v keši Obrázek se nenačítá, jelikož má fetch=LAZY V dotazu se využívá operátoru IN Zařídí výběr specifikovaných fotek. Ikony, jejichž verze je nižší než aktuální verze, jsou odstraněny z keše Ujistěte se, že PhotoCache je anotován @LocalBean public void cleanCache() { @Schedule(second = "*/10", minute = "*", hour = "*", persistent = false) // Pokud je keš prázdná, není co uklízet if (cache.isEmpty()) { return; } Set<Integer> photoIds = getCachedPhotoIds(); // Vybereme všechny fotky, které jsou v keši. Operátor IN // zařídí výběr specifikovaných fotek. Query query = entityManager.createQuery("select p.name, p.version from Photo p where p.id in :photoIds"); query.setParameter("photoIds", photoIds); // Výsledek je seznam dvojic (name, version) List<Object[]> nameVersionPairs = query.getResultList(); // V cyklu porovnáme aktuální verzi fotky s verzí ikony for (Object[] nameVersionPair : nameVersionPairs) { String photoName = (String) nameVersionPair[0]; int versionInDb = (Integer) nameVersionPair[1]; Photo icon = cache.get(photoName); // Porovnání verzí if (versionInDb > icon.getVersion()) { cache.remove(photoName); logger.info("Icon " + photoName + " removed from the cache"); private Set<Integer> getCachedPhotoIds() { Set<Integer> photoIds = new HashSet<Integer>(); for (Photo photo : cache.values()) { photoIds.add(photo.getId()); return photoIds;
Využití interceptoru K synchronizaci keše lze také využít interceptor Interceptor se nasadí na všechny metody, které modifikují entitu Photo nebo mění jejich počet Výhoda: Odstínění služeb od problematiky kešování Metody na AlbumDAOBean addPhoto(Photo photo) updatePhoto(Photo photo) removePhotos(int[] selectedIds) Metoda PhotoGateway save()
CacheSynchronizer V modulu ejb vytvořte třídu interceptoru CacheSynchronizer Interceptor se řídí následující konvencí ohledně hlaviček metod: add* – vytváří novou fotografii, která je v prvním parametru update* – aktualizuje fotografii, která je v prvním parametru remove* – odstraňuje fotografie, jejichž PK jsou v poli int[] předávaném jako první parametr bez parametrů – např. save(), musí se zavolat metoda na beanu, která vrátí ukládanou entitu package cz.vse.javaee.album; import javax.ejb.EJB; import javax.interceptor.AroundInvoke; import javax.interceptor.InvocationContext; import java.lang.reflect.Method; /** * Tento interceptor je zavěšen na všech servisních metodách, které * mění stav a počet entit Photo. * <p/> * Created by IntelliJ IDEA. * User: zslajchrt * Date: Apr 13, 2010 * Time: 1:47:39 PM */ public class CacheSynchronizer { @EJB private PhotoCache cache; @AroundInvoke public Object onUpdatePhoto(InvocationContext ic) throws Exception { Object result = ic.proceed(); String methodName = ic.getMethod().getName(); if (methodName.startsWith("add") || ic.getMethod().getName().startsWith("update")) { // U 'add' a 'update' očekává první argument entitu syncWithCache((Photo) ic.getParameters()[0]); } else if (methodName.startsWith("remove")) { // U 'remove' očekává první argument int[] s id entit syncWithCache((Integer[]) ic.getParameters()[0]); } else if (ic.getParameters() == null) { Method getter = ic.getTarget().getClass().getMethod("getCurrent"); Photo photo = (Photo) getter.invoke(ic.getTarget()); syncWithCache(photo); } return result; private void syncWithCache(Photo photo) { System.out.println("Removing " + photo.getName() + " from cache"); cache.cleanCache(); private void syncWithCache(Integer[] photoIds) { System.out.println("Removing " + photoIds.length + " photos from cache");
Konfigurace interceptoru na metodách Na všechny "create/update/delete" metody, které pracují s Photo, umístěte anotaci @Interceptors(CacheSynchronizer.class)
Poznámky JBoss může mít problémy s používáním sekvence pro generování ID fotek. Pomůže explicitně určit sekvenci pomocí mapovacích anotací a sekvenci ručně vytvořit v databázi: @SequenceGenerator(name = "PHOTOSEQ", sequenceName = "PHOTOSEQUENCE") použít na třídě Photo @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PHOTOSEQ") použít na getPhoto() CREATE SEQUENCE PHOTOSEQUENCE AS BIGINT START WITH 3000000000
REST Architektonický styl distribuovaných systémů REpresentational State Transfer Přenos reprezentace zdroje na klienta Dotazem na zdroj identifikovaný URL nahraje klient reprezentační stav zdroje. Tento stav může obsahovat URL odkazy na další zdroje Dotazem na jiné URL přechází klient do dalšího stavu, který odpovídá reprezentaci odkazovaného zdroje. Dodatečná (post-hoc) interpretace HTTP metod GET, POST, PUT, DELETE
Příklady REST http://myapp/shop/products Zdrojem je zde seznam výrobků Reprezentací může být HTML, XML nebo JSON http://myapp/shop/products/1029 Zdrojem je zde konkrétní výrobek
Omezení na REST architekturu Client-server Stateless – server neudržuje stav klienta Cacheable – klient může kešovat stav Layered – klient neví o přítomnosti mezivrstev Možnost přesunu části kódu serveru na klienta (JavaScript) Jednotné rozhraní pro komunikaci URI ke stavu jsou připojena metadata, např. MIME stav může obsahovat linky na asociované zdroje
Ovládání keše pomocí REST http://localhost:8089/web/rs/cache URL zdroje – keše Metoda GET na tomto zdroji vyčistí keš V modulu web vytvoříme třídu CacheResource @Path("cache") – URL fragment pro tento zdroj @Stateless – chování odpovídá bezstavovému EJB @Produces("text/plain") – MIME reprezentace zdroje Injektáž @EJB PhotoCache photoCache Metoda cleanCache deleguje na PhotoCache @GET – specifikuje, že GET bude směrována sem @Path("cache") @Stateless @Produces("text/plain") public class CacheResource { @EJB private PhotoCache photoCache; @GET public String cleanCache() { photoCache.cleanCache(); return "OK"; }
Úprava WEB-INF/web.xml <servlet> <servlet-name>JerseyWebApplication</servlet-name> <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>cz.vse.javaee.album</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <url-pattern>/rs/*</url-pattern> </servlet-mapping>
Domácí úkol
Dodatek: Aplikační vrstva
Dodatek: Prezentační vrstva
Dodatek: Diagram stránek