Tartalmi kivonat
2013. Július Informatikai Navigátor Érdekes Java Programozói könyvtárak Gondolatok a szoftverek használatáról és fejlesztéséről 9. szám Informatikai Navigator Gondolatok a szoftverek használatáról és fejlesztéséről Tartalomjegyzék 1. JAXB - A Java és XML összekötése 3 2. BeanShell - Könnyűsúlyú scriptek Java nyelvbe ágyazva 32 3. Apache Commons - BeanUtils 44 4. Apache Commons - Lang 53 5. Jodaorg - Programozás dátumokkal és időkkel 67 6. Apache Commons - Virtual File System 78 7. Apache Commons - FileUpload 87 8. Apache Commons - IO 94 9. Az Active Directory elérése Főszerkesztő: Nyiri Imre (imre.nyiri@gmailcom) 2 104 Java programozói könyvtár 1. JAXB - A Java és XML összekötése JAXB - A Java és XML összekötése Az XML adatok és a Java objektumok közötti leképzés fontos eszköze egy korszerű fejlesztői környezetnek. Sok olyan Java API létezik, amelyek XML-t kezelnek, de itt most az a kérdés,
hogy egy XML-ben tárolt adatszerkezetet hogyan konvertálunk oda-vissza egy alkalmas Java objektumba. Ezt oldja meg a JDK 1.6 óta beépített részként működő JAXB könyvtár A JAXB1 az XML adatok és a Java objektumok közötti leképzést valósítja meg, aminek nyilvánvaló előnye az, hogy az XML adatstruktúrákat Java objektumként tudjuk kezelni és fordítva. A Java sok helyen felhasználja ezt a beépített szolgáltatást, erre jó példa az JAX-WS webservice technológia XML↔Java leképzése. A leképzés azt jelenti, hogy a Java objektum adattagjait (vagy más néven a property-ket) leképezzük (ezt nevezzük binding-nak) az XML tagekre. Ezt a feladatot a JAXB kifinomultan és rugalmasan képes ellátni. A fejlesztést végezhetjük a Java osztályokból kiindulva, de lehetséges az is, hogy az XML sémák alapján generáljuk a befogadó objektumok osztályait. A következő egyszerű példa az alapszintű használatba vezet be. Bevezető példa A Java
osztályok osztályt annotáljuk a JAXB-os „@”-okkal. Tekintettel arra, hogy az Employee class reprezentálja az egész objektum kezdetét, így az őt leíró XML gyökérelemének is ez felel meg. Ezt reprezentálja a @XmlRootElement, aminek paraméterében még azt is megadtuk, hogy ez a gyökér XML tag az org.cegtest XML névtérben lesz Az @XmlType annotáció azt mondja meg, hogy ez a class egyben egy XSD-beli típusnév is lenne, amit láthatunk is, amennyiben XML sémát szeretnénk a későbbiekben legenerálni. A névteret itt is meg kell adni. // 1−1. P r o g r a m l i s t a : Employee j a v a package o r g . c s t e s t ; import j a v a x . xml bind a n n o t a t i o n XmlElement ; import j a v a x . xml bind a n n o t a t i o n å XmlRootElement ; import j a v a x . xml bind a n n o t a t i o n XmlType ; @XmlRootElement ( namespace = " o r g . c e g t e s t " ) @XmlType ( namespace = " o r g . c e g t e s t " ) public c l a s s
Employee { @XmlElement Address a d d r e s s ; Legyen a célunk az, hogy az 1-1. Programlistán @XmlElement ( namespace = " o r g . c e g t e s t " ) látható Employee class objektumait XML forS t r i n g name ; mátumra alakítsuk (perzisztáljuk). Amint lát@XmlElement juk ez az osztály egy egyszerű JavaBean (más String sex ; szavakkal POJO 2 ), aminek van néhány adatme@XmlElement zője, köztük az egyik – az Address class – maga int kor ; is egy osztály, amit az 1-2. Programlista mu@XmlElement tat. Mindez igazán egyszerű, de most szeretnénk double s u l y ; egy olyan XML modellt rendelni ehhez, amibe } Most az Employee mindegyik adattagját elegy Employee példány elmenthető. A JAXB 20 óta mindez úgy valósítható meg, hogy a Java láttuk @XmlElement jelzéssel, ami azt jelenti, 1 2 JAXB=Java API for XML Binding POJO=Plain Old Java Object 3 Java programozói könyvtár hogy ezen mezők mindegyikének egy-egy XML tag-et szeretnénk megfeleltetni.
A tag neve a bean property neve lesz, de egy esetleges name annotáció attribútum megadással explicit is megadhatjuk, hogy mi legyen az adott XML címke neve. Esetünkben most csak a namespace paramétert használjuk próbaként, hagyjuk, hogy az XML tag nevek automatikusan képződjenek a class adatmezők nevéből. Az Address class annotálása ezek után már érthető JAXB - A Java és XML összekötése gét annak, hogy kipróbáljuk a JAXB Java ObjectXML leképzési lehetőségét, amit marshallingnak nevezünk. Nézzük is meg a használatát! Az XML előállítása (marshaller) Az 1-3. Programlista a JAXB használatát szemlélteti, feladata az, hogy egy 15-24 sorok között előállított e Employee objektumot XML fájlba mentse. Ehhez először a 26 sorban egy jaxb// 1−2 P r o g r a m l i s t a : Address j a v a Context objektum előállítása szükséges, amely az Employee class mentén ad egy kezelő konpackage o r g . c s t e s t ; textust, ezért volt
szükséges ezt a newInstance() import j a v a x . xml bind a n n o t a t i o n XmlElement ; metódusban paraméterül átadni. A 27 sorban import j a v a x . xml bind a n n o t a t i o n XmlType ; egy általános eljárással lekérünk a kontextustól @XmlType ( namespace = " o r g . c e g t e s t " ) egy jaxbMarshaller objektumot, ami a tényleges public c l a s s Address { Java ObjectXML átalakítást fogja végezni. A @XmlElement 28. sor nem kötelező lépés, de mi azt szeretnénk, String street ; hogy az előállított XML szépen formázva nézzen @XmlElement ki. A 30 sor egy xmlFile nevű File objektum, i n t houseNumber ; } ahova majd az eredményül kapott XML-t mentAz eddigiekben odáig jutottunk, hogy az jük. Végül a 30 sorban elvégezzük a feladatot, Employee és Address osztályokat elláttuk JAXB azaz az e objektumot perzisztáljuk az xmlFile annotációkkal, ezzel megteremtettük a lehetősé- helyre. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
19 20 21 22 23 24 25 26 27 // 1−3. P r o g r a m l i s t a : TestJAXBMarshaller j a v a package o r g . c s t e s t ; import import import import java . io F i l e ; j a v a x . xml bind JAXBContext ; j a v a x . xml bind JAXBException ; j a v a x . xml bind M a r s h a l l e r ; public c l a s s TestJAXBMarshaller { public s t a t i c void main ( S t r i n g [ ] a r g s ) throws JAXBException { Address a = new Address ( ) ; a . houseNumber = 1 2 ; a . s t r e e t = " Váci ␣ út " ; Employee e = new Employee ( ) ; e . address = a ; e . kor = 3 2 ; e . name = "Alma␣ F e r e n c " ; e . sex = " f é r f i " ; e . suly = 88; JAXBContext j a x b C o n t e x t = JAXBContext . n e w I n s t a n c e ( Employee c l a s s ) ; Marshaller jaxbMarshaller = jaxbContext . c r e a t e M a r s h a l l e r ( ) ; 4 Java programozói könyvtár 28 29 30 31 32 } } JAXB - A Java és XML összekötése j a x b M a r s h a l l e r . s e t P r o p e r
t y ( M a r s h a l l e r JAXB FORMATTED OUTPUT, Boolean TRUE) ; F i l e x m l F i l e = new F i l e ( " /home/ t a n u l a s /xml/ Employee . xml" ) ; j a x b M a r s h a l l e r . marshal ( e , x m l F i l e ) ; A program lefuttatása után az 1-4. Prog- rendelkező objektumba pakoljuk az XML outramlista mutatja a legyártott Employeexml fájl putot, ilyen lehet például a StringWriter class, tartalmát. Az alábbiakat javasoljuk megfigyelni: aminek a használatát a 1-5 Programlistáról tanulmányozhatjuk Ekkor a marshal() metódus • A class nevek alapértelmezetten kisbetűvel a writer objektumba teszi az eredményt, ahonkezdődnek. nan azt String vagy StringBuffer objektumként is lekérhetjük. • Az adatmező nevek teljesen megmarad- // 1−5. P r o g r a m l i s t a : S t r i n g W r i t e r nak. • Az XML névtér minden olyan tag-hez bekerült, ahol ezt kértük a „@”-ok megadásánál. // 1 −4. P r o g r a m l i s t a : Employee xml <?xml
version=" 1 . 0 " e n c o d i n g="UTF−8" standalone=" y e s " ?å > <n s 2 : e m p l o y e e x m l n s : n s 2=" o r g . c e g t e s t "> <a d d r e s s> < s t r e e t>V á c i ú t</ s t r e e t> <houseNumber>12</ houseNumber> </ a d d r e s s> <n s 2 : n a m e>Alma F e r e n c</ n s 2 : n a m e> <s e x> f é r f i </ s e x> <k o r>32</ k o r> <s u l y>8 8 . 0</ s u l y> </ n s 2 : e m p l o y e e> Az esetek jelentős részénél nem mindig fájlba szeretnénk tenni az előállított XML-t, ezért a marshal() metódusnak is több alakja van. Tipikus használat, hogy egy Writer interface-szel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 S t r i n g W r i t e r w r i t e r = new S t r i n g W r i t e r ( ) ; j a x b M a r s h a l l e r . ma rs ha l ( e , w r i t e r ) ; String data = writer . toString () ; StringBuffer dataBuffer = writer .
getBuffer () ; Az XML visszaolvasása (unmarshaller) Most végezzük el a fordított feladatot, azaz írjunk egy olyan JAXB alapú programot, ami az XMLJava Object beolvasást (unmarshalling) valósítja meg. Mindezt végezzük el a most előállított Employeexml fájlra Amennyiben nem lenne megfelelően „@”-olt (azaz szebben fogalmazva: annotált) befogadó Java class, akkor azt el kéne készítenünk, de ezek már itt vannak: Emloyee, Address. A feladat megoldását az 1-6 Programlista mutatja. // 1−6. P r o g r a m l i s t a : TestJAXBUnmarshaller j a v a package o r g . c s t e s t ; import import import import java . io F i l e ; j a v a x . xml bind JAXBContext ; j a v a x . xml bind JAXBException ; j a v a x . xml bind U n m a r s h a l l e r ; public c l a s s TestJAXBUnmarshaller { public s t a t i c void main ( S t r i n g [ ] a r g s ) throws JAXBException { JAXBContext j a x b C o n t e x t = JAXBContext . n e w I n s t a n c e ( Employee c l a s s ) ;
Unmarshaller jaxbUnmarshaller = jaxbContext . createUnmarshaller ( ) ; F i l e x m l F i l e = new F i l e ( " /home/ t a n u l a s /xml/ Employee . xml" ) ; Employee e = ( Employee ) j a x b U n m a r s h a l l e r . unmarshal ( x m l F i l e ) ; 5 Java programozói könyvtár 19 20 21 22 } } System . out p r i n t l n ( e name ) ; System . out p r i n t l n ( e a d d r e s s s t r e e t ) ; A 15. sorban a már ismert módon egy JAXBContext objektumot készítünk, amiben csak az Employee class játszik szerepet. Ezt azért emeltük most ki, mert általában több osztály is megadható lenne a gyártómetódusnak, erre majd a későbbiekben mutatunk példát. A 16 sorban természetesen most egy Unmarshaller objektumot kérünk le, hogy azt a 17. sorban megadott fájlra működtessük. Magát a beolvasást a 18 sor unmarshal() metódusa végzi, amelynek használatában nincs semmi különleges. Az utolsó 2 sor csak tesztelésként kiírja a képernyőre az XML
fájlból létrehozott objektum 2 mezőjének az értékét. Az XMLJava Object átalakításnál is gyakori eset, amikor nem fájlból érkezik az input XML, ezért az unmarshal() metódusnak is sok alakja van. A gyakorlatban talán az egyik leghasznosabb esetet mutatja az 1-7. Programlista, ahol egy Java String objektumban van az XML, amit egy StringReader objektum közbeiktatásával már át is adhatunk a metódusnak. // 1−7. P r o g r a m l i s t a : S t r i n g R e a d e r S t r i n g x m l S t r i n g = beolvasFromXMLFile ( ) ; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 JAXB - A Java és XML összekötése S t r i n g R e a d e r r e a d e r = new S t r i n g R e a d e r ( å xmlString ) ; Employee em = ( Employee ) j a x b U n m a r s h a l l e r . å unmarshal ( r e a d e r ) ; Az XML séma (XSD) előállítása Alapvetően fontos az XML világban, hogy annak a sémáját is lehetőleg birtokoljuk, így annak előállításához a JAXB a schemagen
parancsot biztosítja, ami a JDK része. Amennyiben a java/javac elérhető, úgy ez a parancs is rendelkezésre áll. Használata egyszerű, mindössze meg kell adnunk azt a Java forrásfájlt, amiben egy JAXB annotált class található. Az Employee class-ra esetünkben ez a parancs futtatható: schemagen −cp / o p t / n e t b e a n s / m y p r o j e c t s / T e s t P r o j e c t s /å J a v a T e s t / s r c / o p t / n e t b e a n s / m y p r o j e c t s /å T e s t P r o j e c t s / J a v a T e s t / s r c / o r g / c s / t e s t / Employee . å java A -cp kapcsolót azért adtuk meg, hogy a beágyazott Address osztályt is megtalálja a parancs, hiszen ez a séma része kell legyen. A parancs lefutása után az 1-8 Programlista által tartalmazott sémát kapjuk meg. // 1 −8. P r o g r a m l i s t a : Employee XML séma <?xml version=" 1 . 0 " e n c o d i n g="UTF−8" standalone=" y e s " ?> <x s : s c h e m a version=" 1 . 0
" t a r g e t N a m e s p a c e=" o r g c e g t e s t " x m l n s : t n s=" o r g c e g t e s t " x m l n s : x s=" h t t p : //www w3 o r g å / 2 0 0 1 /XMLSchema"> <x s : e l e m e n t name=" e m p l o y e e " t y p e=" t n s : e m p l o y e e " /> <x s : c o m p l e x T y p e name=" e m p l o y e e "> <x s : s e q u e n c e> <x s : e l e m e n t name=" a d d r e s s " t y p e=" t n s : a d d r e s s " minOccurs=" 0 " /> <x s : e l e m e n t name="name" t y p e=" x s : s t r i n g " form=" q u a l i f i e d " minOccurs=" 0 " /> <x s : e l e m e n t name=" s e x " t y p e=" x s : s t r i n g " minOccurs=" 0 " /> <x s : e l e m e n t name=" k o r " t y p e=" x s : i n t " /> <x s : e l e m e n t name=" s u l y " t y p e=" x s : d o u b
l e " /> </ x s : s e q u e n c e> </ x s : c o m p l e x T y p e> <x s : c o m p l e x T y p e name=" a d d r e s s "> <x s : s e q u e n c e> <x s : e l e m e n t name=" s t r e e t " t y p e=" x s : s t r i n g " minOccurs=" 0 " /> <x s : e l e m e n t name=" houseNumber " t y p e=" x s : i n t " /> </ x s : s e q u e n c e> </ x s : c o m p l e x T y p e> </ x s : s c h e m a> 6 Java programozói könyvtár JAXB - A Java és XML összekötése Ránézésre tetszetős, az XSD típusok szépen leképződtek a megfelelő Java típusokról. A szerkezete is megfelelő A 11 sor form="qualified" jelzése azt jelenti, hogy ez a tag a targetNamespace-ben (org.cegtest) van Az address complexType a várt módon, külön nevesítve megtalálható és a 10 sorban korrekt módon került használatra. szerencsére a JAXB erre is tartalmaz egy JDK parancsot,
aminek a neve xjc. A sémák általában valamilyen szabvány részeként állnak rendelkezésünkre, például: UBL (Universal Business Language), ebXML, OFX (Open Financial Exchange), de az is lehet, hogy a rendszerek közötti kommunikáció egy sémával, platformfüggetlen módon volt csak definiálható, ami nagy előny. Az Employee class alapján generált séma felhasználásával generáljuk le a Java osztályokat, Java osztályok generálása XML sémából azaz tegyünk úgy mintha azok még nem létezA gyakorlatban az is sokszor előfordul, hogy nének: x j c Employee . xsd az XSD már rendelkezésünkre áll. Nagyon sok munkát jelentene a megfelelő befogadó Java oszAz eredményt az 1-9., 1-10 és 1-11 Progtály létrehozása és JAXB „@”-ok elhelyezése, de ramlisták tartalmazzák 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 // 1−9. P r o g r a m l i s t a : Az x j c á l t a l g e n e r á l t Address
o s z t á l y // // This f i l e was g e n e r a t e d by t h e JavaTM A r c h i t e c t u r e f o r XML B i n d i n g (JAXB) R e f e r e n c e å Implementation , v2 .24 −2 // See <a h r e f =" h t t p : / / j a v a . sun com/ xml / j a x b "> h t t p : / / j a v a sun com/ xml / j a x b </a> // Any m o d i f i c a t i o n s t o t h i s f i l e w i l l be l o s t upon r e c o m p i l a t i o n o f t h e s o u r c e schema . // Generated on : 2 0 1 3 . 0 3 1 9 a t 0 6 : 4 9 : 0 4 PM GMT // package t e s t . c e g o r g ; import j a v a x . xml bind a n n o t a t i o n XmlAccessType ; import j a v a x . xml bind a n n o t a t i o n XmlAccessorType ; import j a v a x . xml bind a n n o t a t i o n XmlType ; /∗ ∗ ∗ <p>Java c l a s s f o r a d d r e s s complex t y p e . ∗ ∗ <p>The f o l l o w i n g schema f r a g m e n t s p e c i f i e s t h e e x p e c t e d c o n t e n t c o n t a i n e d ∗ within t his class . ∗ ∗
<pre> ∗ & l t ; complexType name="a d d r e s s"> ∗ & l t ; complexContent> ∗ & l t ; r e s t r i c t i o n b a s e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} anyType"> ∗ &l t ; sequence> ∗ & l t ; e l e m e n t name=" s t r e e t " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} s t r i n g " minOccurså ="0"/> ∗ & l t ; e l e m e n t name="houseNumber " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} i n t "/> ∗ &l t ; / sequence> ∗ &l t ;/ r e s t r i c t i o n > ∗ & l t ; / complexContent> ∗ & l t ; / complexType> ∗ </pre> ∗ ∗ ∗/ @XmlAccessorType ( XmlAccessType . FIELD) @XmlType ( name = " a d d r e s s " , propOrder = { 7 Java programozói könyvtár 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
78 79 80 81 82 83 84 85 86 87 1 2 3 4 5 6 7 8 9 10 11 12 13 14 JAXB - A Java és XML összekötése " street " , " houseNumber " }) public c l a s s Address { protected S t r i n g s t r e e t ; protected i n t houseNumber ; /∗ ∗ ∗ Gets t h e v a l u e o f t h e s t r e e t p r o p e r t y . ∗ ∗ @return p o s s i b l e o b j e c t i s { @ l i n k S t r i n g } ∗ ∗/ public S t r i n g g e t S t r e e t ( ) { return s t r e e t ; } /∗ ∗ ∗ Sets the value of the s t r e e t property . ∗ ∗ @param v a l u e a l l o w e d o b j e c t i s { @ l i n k S t r i n g } ∗ ∗/ public void s e t S t r e e t ( S t r i n g v a l u e ) { this . s t r e e t = value ; } /∗ ∗ ∗ Gets t h e v a l u e o f t h e houseNumber p r o p e r t y . ∗ ∗/ public i n t getHouseNumber ( ) { return houseNumber ; } } /∗ ∗ ∗ S e t s t h e v a l u e o f t h e houseNumber p r o p e r t y . ∗ ∗/ public void setHouseNumber ( i n t v a l u e ) { t h i s .
houseNumber = v a l u e ; } // 1 −10. P r o g r a m l i s t a : Az x j c á l t a l g e n e r á l t Employee o s z t á l y // // This f i l e was g e n e r a t e d by t h e JavaTM A r c h i t e c t u r e f o r XML B i n d i n g (JAXB) R e f e r e n c e å Implementation , v2 .24 −2 // See <a h r e f =" h t t p : / / j a v a . sun com/ xml / j a x b "> h t t p : / / j a v a sun com/ xml / j a x b </a> // Any m o d i f i c a t i o n s t o t h i s f i l e w i l l be l o s t upon r e c o m p i l a t i o n o f t h e s o u r c e schema . // Generated on : 2 0 1 3 . 0 3 1 9 a t 0 6 : 4 9 : 0 4 PM GMT // package t e s t . c e g o r g ; import import import import 8 j a v a x . xml bind a n n o t a t i o n XmlAccessType ; j a v a x . xml bind a n n o t a t i o n XmlAccessorType ; j a v a x . xml bind a n n o t a t i o n XmlElement ; j a v a x . xml bind a n n o t a t i o n XmlRootElement ; Java programozói könyvtár 15 16 17 18 19 20 21 22
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 JAXB - A Java és XML összekötése import j a v a x . xml bind a n n o t a t i o n XmlType ; /∗ ∗ ∗ <p>Java c l a s s f o r employee complex t y p e . ∗ ∗ <p>The f o l l o w i n g schema f r a g m e n t s p e c i f i e s t h e e x p e c t e d c o n t e n t c o n t a i n e d ∗ within t his class . ∗ ∗ <pre> ∗ & l t ; complexType name="employee"> ∗ & l t ; complexContent> ∗ & l t ; r e s t r i c t i o n b a s e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} anyType"> ∗ &l t ; sequence> ∗ & l t ; e l e m e n t name="a d d r e s s " t y p e ="{ o r g . c e g t e s t } a d d r e s s " minOccurs="0"/> ∗ & l t ; e l e m e n t name="name" t y p e ="{ h t t p : / /www. w3 o
r g /2001/XMLSchema} s t r i n g " minOccurs="0"å form=" q u a l i f i e d "/> ∗ & l t ; e l e m e n t name="s e x " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} s t r i n g " minOccurså ="0"/> ∗ & l t ; e l e m e n t name="k o r " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} i n t "/> ∗ & l t ; e l e m e n t name=" s u l y " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} d o u b l e "/> ∗ &l t ; / sequence> ∗ &l t ;/ r e s t r i c t i o n > ∗ & l t ; / complexContent> ∗ & l t ; / complexType> ∗ </pre> ∗ ∗ ∗/ @XmlRootElement ( namespace = " o r g . c e g t e s t " ) @XmlAccessorType ( XmlAccessType . FIELD) @XmlType ( name = " employee " , propOrder = { " address " , "name" , " sex " , " kor " , " suly " })
public c l a s s Employee { protected Address a d d r e s s ; @XmlElement ( namespace = " o r g . c e g t e s t " ) protected S t r i n g name ; protected S t r i n g s e x ; protected i n t k o r ; protected double s u l y ; /∗ ∗ ∗ Gets t h e v a l u e o f t h e a d d r e s s p r o p e r t y . ∗ ∗ @return p o s s i b l e o b j e c t i s { @ l i n k Address } ∗ ∗/ public Address g e t A d d r e s s ( ) { return a d d r e s s ; } /∗ ∗ ∗ Sets the value of the address property . ∗ ∗ @param v a l u e a l l o w e d o b j e c t i s { @ l i n k Address } ∗ ∗/ 9 Java programozói könyvtár 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 public void s e t A d d r e s s ( Address v a l u e ) { this . address = value ; } /∗ ∗ ∗ Gets t h e v a l u e o f t h e
name p r o p e r t y . ∗ ∗ @return p o s s i b l e o b j e c t i s { @ l i n k S t r i n g } ∗ ∗/ public S t r i n g getName ( ) { return name ; } /∗ ∗ ∗ S e t s t h e v a l u e o f t h e name p r o p e r t y . ∗ ∗ @param v a l u e a l l o w e d o b j e c t i s { @ l i n k S t r i n g } ∗ ∗/ public void setName ( S t r i n g v a l u e ) { t h i s . name = v a l u e ; } /∗ ∗ ∗ Gets t h e v a l u e o f t h e s e x p r o p e r t y . ∗ ∗ @return p o s s i b l e o b j e c t i s { @ l i n k S t r i n g } ∗ ∗/ public S t r i n g g e t S e x ( ) { return s e x ; } /∗ ∗ ∗ Sets the value of the sex property . ∗ ∗ @param v a l u e a l l o w e d o b j e c t i s { @ l i n k S t r i n g } ∗ ∗/ public void s e t S e x ( S t r i n g v a l u e ) { this . sex = value ; } /∗ ∗ ∗ Gets t h e v a l u e o f t h e k o r p r o p e r t y . ∗ ∗/ public i n t getKor ( ) { return k o r ; } /∗ ∗ ∗ Sets the value of the kor property . ∗ ∗/ public
void s e t K o r ( i n t v a l u e ) { this . kor = value ; 10 JAXB - A Java és XML összekötése Java programozói könyvtár 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 JAXB - A Java és XML összekötése } /∗ ∗ ∗ Gets t h e v a l u e o f t h e s u l y p r o p e r t y . ∗ ∗/ public double g e t S u l y ( ) { return s u l y ; } } /∗ ∗ ∗ Sets the value of the suly property . ∗ ∗/ public void s e t S u l y ( double v a l u e ) { this . suly = value ; } // 1 −11. P r o g r a m l i s t a : Az x j c á l t a l g e n e r á l t O b j e c t F a c t o r y o s z t á l y // // This f i l e was g e n e r a t e d by t h e JavaTM A r c h i t e c t u r e f o r XML B i n d i n g (JAXB) R e f e r e n c e å Implementation , v2 .24 −2 // See <a h r e f =" h t t p : / / j a v a . sun com/ xml / j a x b
"> h t t p : / / j a v a sun com/ xml / j a x b </a> // Any m o d i f i c a t i o n s t o t h i s f i l e w i l l be l o s t upon r e c o m p i l a t i o n o f t h e s o u r c e schema . // Generated on : 2 0 1 3 . 0 3 1 9 a t 0 6 : 4 9 : 0 4 PM GMT // package t e s t . c e g o r g ; import import import import j a v a x . xml bind JAXBElement ; j a v a x . xml bind a n n o t a t i o n XmlElementDecl ; j a v a x . xml bind a n n o t a t i o n XmlRegistry ; j a v a x . xml namespace QName ; /∗ ∗ ∗ This o b j e c t c o n t a i n s f a c t o r y methods f o r each ∗ Java c o n t e n t i n t e r f a c e and Java e l e m e n t i n t e r f a c e ∗ generated in the t e s t . ceg org package ∗ <p>An O b j e c t F a c t o r y a l l o w s you t o p r o g r a m a t i c a l l y ∗ c o n s t r u c t new i n s t a n c e s o f t h e Java r e p r e s e n t a t i o n ∗ f o r XML c o n t e n t . The Java r e p r e s e n t a t i o n o f XML ∗ c o n t
e n t can c o n s i s t o f schema d e r i v e d i n t e r f a c e s ∗ and c l a s s e s r e p r e s e n t i n g t h e b i n d i n g o f schema ∗ t y p e d e f i n i t i o n s , e l e m e n t d e c l a r a t i o n s and model ∗ groups . F a c t o r y methods f o r each o f t h e s e a r e ∗ provided in t h i s c l a s s . ∗ ∗/ @XmlRegistry public c l a s s O b j e c t F a c t o r y { private f i n a l s t a t i c QName Employee QNAME = new QName( " o r g . c e g t e s t " , " employee " ) ; /∗ ∗ ∗ C r e a t e a new O b j e c t F a c t o r y t h a t can be used t o c r e a t e new i n s t a n c e s o f schema d e r i v e d å c l a s s e s f o r package : t e s t . ceg org ∗ ∗/ public O b j e c t F a c t o r y ( ) { 11 Java programozói könyvtár 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 JAXB - A Java és XML összekötése } /∗ ∗ ∗ C r e a t e an i n s t a n c e o f { @ l i n k Employee
} ∗ ∗/ public Employee c r e a t e E m p l o y e e ( ) { return new Employee ( ) ; } /∗ ∗ ∗ C r e a t e an i n s t a n c e o f { @ l i n k Address } ∗ ∗/ public Address c r e a t e A d d r e s s ( ) { return new Address ( ) ; } /∗ ∗ ∗ C r e a t e an i n s t a n c e o f { @ l i n k JAXBElement }{ @code <}{ @ l i n k Employee }{ @code >}} ∗ ∗/ @XmlElementDecl ( namespace = " o r g . c e g t e s t " , name = " employee " ) public JAXBElement<Employee> c r e a t e E m p l o y e e ( Employee v a l u e ) { return new JAXBElement<Employee >( Employee QNAME, Employee . c l a s s , null , v a l u e ) ; } } Szeretnénk kiemelni, hogy az Employee classhoz mi írtuk oda a @XmlRootElement annotációt, mert az xjc ezt valami miatt nem tette meg. Ettől eltekintve korrekt Java forrás jött létre, gyakorlatilag kézzel is ezt írtuk meg korábban. Az Employee.xml XML fájl beolvasása ezekkel a generált osztályokkal természetesen
ugyanúgy működik, ha kipróbáljuk. Az ObjectFactory osztály gyártómetódusokat ad az Employee és Address objektumok előállítására, de ezek csak kényelmi funkciók import j a v a x . xml bind a n n o t a t i o n å XmlRootElement ; import j a v a x . xml bind a n n o t a t i o n XmlType ; @XmlRootElement ( namespace = " o r g . c e g t e s t " , name = " Dolgozo " ) @XmlType ( namespace = " o r g . c e g t e s t " ) public c l a s s Employee { @XmlElement ( name=" cim " , namespace = " h t t p : / / o r g . c s a d d r e s s " ) Address a d d r e s s ; @XmlElement ( name=" nev " , namespace = " o r g . c e g t e s t " ) S t r i n g name ; @XmlElement ( name="nem" ) String sex ; Néhány apró változtatás A példa befejezéseként teszünk néhány változtatást az eredeti 2 Java osztályunkon, majd megnézzük, hogy milyen sémát lehet velük generálni. A megváltoztatott 2
bean-t az 1-12. és 1-13 Programlisták mutatják. @XmlElement int kor ; } @XmlElement double s u l y ; // 1 −12. P r o g r a m l i s t a : Employee j a v a // 1 −13. P r o g r a m l i s t a : Address j a v a package o r g . c s t e s t t t t ; package o r g . c s t e s t t t t ; import j a v a x . xml bind a n n o t a t i o n XmlElement ; import j a v a x . xml bind a n n o t a t i o n XmlElement ; 12 Java programozói könyvtár import j a v a x . xml bind a n n o t a t i o n XmlType ; @XmlType ( name=" cim " , namespace = " o r g . c e g t e s t " ) public c l a s s Address { @XmlElement ( name=" u t c a " ) String street ; } @XmlElement ( name=" hazszam " ) i n t houseNumber ; A változtatások ezek voltak: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 1 2 3 4 5 6 7 8 9 JAXB - A Java és XML összekötése • Ahol nem volt magyar az adatmező neve, ott magyar name értékre
állítottuk a generált címke nevet. Példa: streetutca • Az address mezőt egy minden eddigitől eltérő másik, http://org.csaddress nevű névtérbe tettük. A schemagen parancs futtatása után most 2 XSD fájl keletkezett, amit az 1-14. és 1-15 Programlisták mutatnak. // 1 −14. P r o g r a m l i s t a : schema1 xsd <?xml version=" 1 . 0 " e n c o d i n g="UTF−8" standalone=" y e s " ?> <x s : s c h e m a version=" 1 . 0 " t a r g e t N a m e s p a c e=" o r g c e g t e s t " x m l n s : n s 1=" h t t p : // o r g c s a d d r e s s " x m l n s : t n s=" o r g c e g å t e s t " xm <x s : i m p o r t namespace=" h t t p : // o r g . c s a d d r e s s " s c h e m a L o c a t i o n=" schema2 xsd " /> <x s : e l e m e n t name=" D o l g o z o " t y p e=" t n s : e m p l o y e e " /> <x s : c o m p l e x T y p e name=" e m p l o
y e e "> <x s : s e q u e n c e> <x s : e l e m e n t r e f=" n s 1 : c i m " minOccurs=" 0 " /> <x s : e l e m e n t name=" nev " t y p e=" x s : s t r i n g " form=" q u a l i f i e d " minOccurs=" 0 " /> <x s : e l e m e n t name="nem" t y p e=" x s : s t r i n g " minOccurs=" 0 " /> <x s : e l e m e n t name=" k o r " t y p e=" x s : i n t " /> <x s : e l e m e n t name=" s u l y " t y p e=" x s : d o u b l e " /> </ x s : s e q u e n c e> </ x s : c o m p l e x T y p e> <x s : c o m p l e x T y p e name=" cim "> <x s : s e q u e n c e> <x s : e l e m e n t name=" u t c a " t y p e=" x s : s t r i n g " minOccurs=" 0 " /> <x s : e l e m e n t name=" hazszam " t y p e=" x s : i n t " /> </ x s : s e q u e n c e>
</ x s : c o m p l e x T y p e> </ x s : s c h e m a> // 1 −15. P r o g r a m l i s t a : schema2 xsd <?xml version=" 1 . 0 " e n c o d i n g="UTF−8" standalone=" y e s " ?> <x s : s c h e m a version=" 1 . 0 " t a r g e t N a m e s p a c e=" h t t p : // o r g c s a d d r e s s " x m l n s : n s 1=" o r g c e g t e s t " x m l n s : x s=" h t t p : //www å w3 . o r g ␣ ␣<x s : i m p o r t ␣ namespace=" o r g . c e g t e s t " ␣ s c h e m a L o c a t i o n=" schema1 xsd "/> ␣ ␣<x s : e l e m e n t ␣name=" cim " ␣ t y p e=" n s 1 : c i m "/> </x s : s c h e m a > Mit tanulhatunk ebből? Az első, amit észrevehetünk, hogy az XML tag neveket megváltoztattuk, azaz nem automatikusan a Java bean property névből képződnek ott, ahol eredetileg angol mező nevek voltak. A másik lényeges és megjegyzésre érdemes
momentum az, hogy a 2 névtér miatt 2 darab XSD keletkezett, amik egymást beimportálják. Ezzel lehetővé vált, hogy mindkét targetNamespace létezzen és a megfelelően adjuk meg a névtérbe tartozó XML séma- típust. Az XML sémák fordítása Az előző példában minden alapvető dolgot megtanultunk, ezért a továbbiakban csak a tudásunkat mélyítjük egy kicsit tovább. Az 1-16 Programlistán látható XML séma egy minden nap használt, produktív megoldás része. A következőkben megnézzük, hogy milyen Java kód gene13 Java programozói könyvtár rálható erre és az egyes típusokhoz milyen Java típusok tartoznak majd. A sémával kapcsolatosan a következőkre hívjuk fel a figyelmet: • Van benne 4 darab complexType • A targetNamespace: urn:jaxbtest/xsd2java JAXB - A Java és XML összekötése mal kell, hogy előforduljon. A minOccurs="0" azt jelenti, hogy 0 darab is lehet 1 XML-ben ebből a tag-ból, míg a
maxOccurs="unbounded" pedig azt engedi, hogy akármennyi. Ekkor itt egy Java tömb vagy List várható majd a generálás eredményeként. • Mindegyik XSD skalár típus megtalálható benne Ennyi előzmény után futtassuk le a következő xjc • Van olyan, ahol a maxOccurs="unbounded", parancsot, ami legyártja a Java class-okat: azaz a címke akárhányszor ismétlődhet. x j c −p c s . o r g j a x b t e s t j a x b T e s t . xsd A -p kapcsolóval azt adtuk meg, hogy a for• Amikor nem adjuk meg egy sémarészhez a minOccurs és maxOccurs attribútumo- rás milyen Java csomagba kerüljön, így a genekat, akkor az elem pontosan 1 alkalom- rált kód az cs/org/jaxb/test könyvtárba került. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 // 1 −16. P r o g r a m l i s t a : j a x b T e s t . xsd <?xml version=" 1 . 0 " e n c o d i n
g="UTF−8" ?> <x s : s c h e m a a t t r i b u t e F o r m D e f a u l t=" u n q u a l i f i e d " e l e m e n t F o r m D e f a u l t=" q u a l i f i e d " t a r g e t N a m e s p a c e=" u r n : j a x b t e s t /å x s d 2 j a v a " x m l n s : x s=" h t t p : //www. w3 o r g / 2 0 0 1 /XMLSchema"> <x s : e l e m e n t name=" s e l e c t " t y p e=" s c h : s e l e c t T y p e " x m l n s : s c h=" u r n : j a x b t e s t / x s d 2 j a v a " /> <x s : c o m p l e x T y p e name=" dataType "> <x s : s e q u e n c e minOccurs=" 0 " maxOccurs=" unbounded "> <x s : e l e m e n t t y p e=" s c h : r o w T y p e " name=" row " x m l n s : s c h=" u r n : j a x b t e s t / x s d 2 j a v a " /> </ x s : s e q u e n c e> </ x s : c o m p l e x T y p e> <x s : c o m p l e x T y p e name=" rowType
"> <x s : s e q u e n c e> <x s : e l e m e n t t y p e=" x s : s t r i n g " name=" t e s t S t r i n g " /> <x s : e l e m e n t t y p e=" x s : i n t e g e r " name=" t e s t I n t e g e r " /> <x s : e l e m e n t t y p e=" x s : i n t " name=" t e s t I n t " /> <x s : e l e m e n t t y p e=" x s : l o n g " name=" t e s t L o n g " /> <x s : e l e m e n t t y p e=" x s : s h o r t " name=" t e s t S h o r t " /> <x s : e l e m e n t t y p e=" x s : d e c i m a l " name=" t e s t D e c i m a l " /> <x s : e l e m e n t t y p e=" x s : f l o a t " name=" t e s t F l o a t " /> <x s : e l e m e n t t y p e=" x s : d o u b l e " name=" t e s t D o u b l e " /> <x s : e l e m e n t t y p e=" x s : b o o l e a n " name=" t e s t B o o l e a n "
/> <x s : e l e m e n t t y p e=" x s : b y t e " name=" t e s t B y t e " /> <x s : e l e m e n t t y p e=" xs:QName " name=" testQName " /> <x s : e l e m e n t t y p e=" x s : d a t e T i m e " name=" t e s t D a t e T i m e " /> <x s : e l e m e n t t y p e=" x s : b a s e 6 4 B i n a r y " name=" t e s t B a s e 6 4 B i n a r y " /> <x s : e l e m e n t t y p e=" x s : h e x B i n a r y " name=" t e s t H e x B i n a r y " /> <x s : e l e m e n t t y p e=" x s : u n s i g n e d I n t " name=" t e s t U n s i g n e d I n t " /> <x s : e l e m e n t t y p e=" x s : u n s i g n e d S h o r t " name=" t e s t U n s i g n e d S h o r t " /> <x s : e l e m e n t t y p e=" x s : u n s i g n e d B y t e " name=" t e s t U n s i g n e d B y t e 2 " /> <x s : e l e m e n t t y p
e=" x s : t i m e " name=" t e s t T i m e " /> <x s : e l e m e n t t y p e=" x s : d a t e " name=" t e s t D a t e " /> <x s : e l e m e n t t y p e=" x s : a n y S i m p l e T y p e " name=" t e s t A n y S i m p l e T y p e " /> <x s : e l e m e n t t y p e=" x s : d u r a t i o n " name=" t e s t D u r a t i o n " /> </ x s : s e q u e n c e> </ x s : c o m p l e x T y p e> <x s : c o m p l e x T y p e name=" c o n t r o l l T y p e "> <x s : s e q u e n c e> <x s : e l e m e n t t y p e=" x s : s t r i n g " name=" theme " /> <x s : e l e m e n t t y p e=" x s : d a t e T i m e " name=" timestamp " /> <x s : e l e m e n t t y p e=" x s : s t r i n g " name=" q u e r y " /> <x s : e l e m e n t t y p e=" x s : s t r i n g " name=" t a b l e " />
<x s : e l e m e n t t y p e=" x s : s t r i n g " name=" d a t a s o u r c e " /> <x s : e l e m e n t t y p e=" x s : l o n g " name=" r o w c n t " /> <x s : e l e m e n t t y p e=" x s : l o n g " name=" r e s u l t s e t i d " /> </ x s : s e q u e n c e> </ x s : c o m p l e x T y p e> <x s : c o m p l e x T y p e name=" s e l e c t T y p e "> <x s : s e q u e n c e> <x s : e l e m e n t t y p e=" s c h : c o n t r o l l T y p e " name=" c o n t r o l l " x m l n s : s c h=" u r n : j a x b t e s t / x s d 2 j a v a " /> <x s : e l e m e n t t y p e=" s c h : d a t a T y p e " name=" d a t a " x m l n s : s c h=" u r n : j a x b t e s t / x s d 2 j a v a " /> </ x s : s e q u e n c e> </ x s : c o m p l e x T y p e> </ x s : s c h e m a> 14 Java programozói könyvtár 1 2 3
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 JAXB - A Java és XML összekötése // 1 −17. P r o g r a m l i s t a : C o n t r o l l T y p e j a v a // // This f i l e was g e n e r a t e d by t h e JavaTM A r c h i t e c t u r e f o r XML B i n d i n g (JAXB) R e f e r e n c e å Implementation , v2 .24 −2 // See <a h r e f =" h t t p : / / j a v a . sun com/ xml / j a x b "> h t t p : / / j a v a sun com/ xml / j a x b </a> // Any m o d i f i c a t i o n s t o t h i s f i l e w i l l be l o s t upon r e c o m p i l a t i o n o f t h e s o u r c e schema . // Generated on : 2 0 1 3 . 0 4 0 6 a t 0 2 : 5 9 : 4 5 PM GMT // package c s . o r g j a x b t e s t ; import import import import import import j a v a x . xml bind a n n o t a t i o n XmlAccessType ; j a v a x . xml bind a n n o t a t i o n XmlAccessorType ; j
a v a x . xml bind a n n o t a t i o n XmlElement ; j a v a x . xml bind a n n o t a t i o n XmlSchemaType ; j a v a x . xml bind a n n o t a t i o n XmlType ; j a v a x . xml d a t a t y p e XMLGregorianCalendar ; /∗ ∗ ∗ <p>Java c l a s s f o r c o n t r o l l T y p e complex t y p e . ∗ ∗ <p>The f o l l o w i n g schema f r a g m e n t s p e c i f i e s t h e e x p e c t e d c o n t e n t c o n t a i n e d w i t h i n t h i s c l a s s . ∗ ∗ <pre> ∗ & l t ; complexType name=" c o n t r o l l T y p e "> ∗ & l t ; complexContent> ∗ & l t ; r e s t r i c t i o n b a s e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} anyType"> ∗ &l t ; sequence> ∗ & l t ; e l e m e n t name="theme " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} s t r i n g "/> ∗ & l t ; e l e m e n t name="timestamp " t y p e ="{ h t t p : / /www. w3 o r g
/2001/XMLSchema} dateTime"/> ∗ & l t ; e l e m e n t name="q u e r y " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} s t r i n g "/> ∗ & l t ; e l e m e n t name=" t a b l e " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} s t r i n g "/> ∗ & l t ; e l e m e n t name="d a t a s o u r c e " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} s t r i n g "/> ∗ & l t ; e l e m e n t name="rowcnt " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} l o n g "/> ∗ & l t ; e l e m e n t name=" r e s u l t s e t i d " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} l o n g "/> ∗ &l t ; / sequence> ∗ &l t ;/ r e s t r i c t i o n > ∗ & l t ; / complexContent> ∗ & l t ; / complexType> ∗ </pre> ∗ ∗ ∗/ @XmlAccessorType ( XmlAccessType . FIELD) @XmlType (
name = " c o n t r o l l T y p e " , propOrder = { " theme " , " timestamp " , " query " , " table " , " datasource " , " rowcnt " , "resultsetid" }) public c l a s s C o n t r o l l T y p e { @XmlElement ( r e q u i r e d = true ) protected S t r i n g theme ; @XmlElement ( r e q u i r e d = true ) @XmlSchemaType ( name = " dateTime " ) protected XMLGregorianCalendar timestamp ; @XmlElement ( r e q u i r e d = true ) 15 Java programozói könyvtár 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 JAXB - A Java és XML összekötése protected S t r i n g query ; @XmlElement ( r e q u i r e d = true ) protected S t r i n g t a b l e ; @XmlElement ( r e q u i r e d = true ) protected S t r i n g d a t a s o u r c e ; protected long rowcnt ; protected long r e
s u l t s e t i d ; . s e t t e r é s g e t t e r metódusok /∗ ∗ ∗ Gets t h e v a l u e o f t h e timestamp p r o p e r t y . ∗ ∗ @return ∗ possible object is ∗ { @ l i n k XMLGregorianCalendar } ∗ ∗/ public XMLGregorianCalendar getTimestamp ( ) { return timestamp ; } } /∗ ∗ ∗ S e t s t h e v a l u e o f t h e timestamp p r o p e r t y . ∗ ∗ @param v a l u e ∗ allowed object is ∗ { @ l i n k XMLGregorianCalendar } ∗ ∗/ public void setTimestamp ( XMLGregorianCalendar v a l u e ) { t h i s . timestamp = v a l u e ; } // 1 −18. P r o g r a m l i s t a : RowType j a v a package c s . o r g j a x b t e s t ; import import import import import import import import import import import import j a v a . math BigDecimal ; j a v a . math B i g I n t e g e r ; j a v a x . xml bind a n n o t a t i o n XmlAccessType ; j a v a x . xml bind a n n o t a t i o n XmlAccessorType ; j a v a x . xml bind a n n o t a t i o n XmlElement ; j a v a x .
xml bind a n n o t a t i o n XmlSchemaType ; j a v a x . xml bind a n n o t a t i o n XmlType ; j a v a x . xml bind a n n o t a t i o n a d a p t e r s HexBinaryAdapter ; j a v a x . xml bind a n n o t a t i o n a d a p t e r s XmlJavaTypeAdapter ; j a v a x . xml d a t a t y p e D u r a t i o n ; j a v a x . xml d a t a t y p e XMLGregorianCalendar ; j a v a x . xml namespace QName ; /∗ ∗ ∗ <p>Java c l a s s f o r rowType complex t y p e . ∗ ∗ <p>The f o l l o w i n g schema f r a g m e n t s p e c i f i e s t h e e x p e c t e d c o n t e n t c o n t a i n e d w i t h i n t h i s c l a s s . ∗ ∗ <pre> ∗ & l t ; complexType name="rowType"> ∗ & l t ; complexContent> ∗ & l t ; r e s t r i c t i o n b a s e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} anyType"> ∗ &l t ; sequence> ∗ & l t ; e l e m e n t name=" t e s t S t r i n g " t y p e ="{ h t t p : /
/www. w3 o r g /2001/XMLSchema} s t r i n g "/> ∗ & l t ; e l e m e n t name=" t e s t I n t e g e r " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} i n t e g e r "/> ∗ & l t ; e l e m e n t name=" t e s t I n t " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} i n t "/> 16 Java programozói könyvtár 32 33 34 35 36 37 38 39 40 41 ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ 42 43 ∗ ∗ 44 ∗ 45 ∗ 46 47 48 ∗ ∗ ∗ 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 JAXB - A Java és XML összekötése & l t ; e l e m e n t name="t e s t L o n g " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} l o n g "/> & l t ; e l e m e n t name=" t e s t S h o r t " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} s h o r t "/> & l t ; e l e
m e n t name="t e s t D e c i m a l " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} d e c i m a l "/> & l t ; e l e m e n t name=" t e s t F l o a t " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} f l o a t "/> & l t ; e l e m e n t name=" t e s t D o u b l e " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} d o u b l e "/> & l t ; e l e m e n t name=" t e s t B o o l e a n " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} b o o l e a n "/> & l t ; e l e m e n t name=" t e s t B y t e " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} b y t e "/> & l t ; e l e m e n t name="testQName" t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema}QName"/> & l t ; e l e m e n t name="t e s t D a t e T i m e " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema}
dateTime"/> & l t ; e l e m e n t name="t e s t B a s e 6 4 B i n a r y " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema}å b a s e 6 4 B i n a r y "/> & l t ; e l e m e n t name="t e s t H e x B i n a r y " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} hexBinary"/> & l t ; e l e m e n t name=" t e s t U n s i g n e d I n t " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema}å u n s i g n e d I n t "/> & l t ; e l e m e n t name="t e s t U n s i g n e d S h o r t " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema}å u n s i g n e d S h o r t "/> & l t ; e l e m e n t name="t e s t U n s i g n e d B y t e 2 " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema}å u n s i g n e d B y t e "/> & l t ; e l e m e n t name="t e s t T i m e " t y p e ="{ h t t p : / /www. w3 o r g
/2001/XMLSchema} time"/> & l t ; e l e m e n t name=" t e s t D a t e " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} d a t e "/> & l t ; e l e m e n t name="te s tA ny S im p l e T y p e " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema}å anySimpleType"/> & l t ; e l e m e n t name=" t e s t D u r a t i o n " t y p e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} d u r a t i o n "/> &l t ; / sequence> &l t ;/ r e s t r i c t i o n > & l t ; / complexContent> & l t ; / complexType> </pre> ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗/ @XmlAccessorType ( XmlAccessType . FIELD) @XmlType ( name = "rowType" , propOrder = { " testString " , " testInteger " , " testInt " , " testLong " , " testShort " , " testDecimal " , " testFloat " , " testDouble " , "
testBoolean " , " testByte " , " testQName " , " testDateTime " , " testBase64Binary " , " testHexBinary " , " testUnsignedInt " , " testUnsignedShort " , " testUnsignedByte2 " , " testTime " , " testDate " , " testAnySimpleType " , " testDuration " }) public c l a s s RowType { @XmlElement ( r e q u i r e d = true ) protected S t r i n g t e s t S t r i n g ; @XmlElement ( r e q u i r e d = true ) protected B i g I n t e g e r t e s t I n t e g e r ; protected i n t t e s t I n t ; protected long t e s t L o n g ; protected short t e s t S h o r t ; @XmlElement ( r e q u i r e d = true ) 17 Java programozói könyvtár 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 JAXB - A Java és XML összekötése protected BigDecimal t e s t D e c i m a l ; protected f l o a
t t e s t F l o a t ; protected double t e s t D o u b l e ; protected boolean t e s t B o o l e a n ; protected byte t e s t B y t e ; @XmlElement ( r e q u i r e d = true ) protected QName testQName ; @XmlElement ( r e q u i r e d = true ) @XmlSchemaType ( name = " dateTime " ) protected XMLGregorianCalendar testDateTime ; @XmlElement ( r e q u i r e d = true ) protected byte [ ] t e s t B a s e 6 4 B i n a r y ; @XmlElement ( r e q u i r e d = true , t y p e = S t r i n g . c l a s s ) @XmlJavaTypeAdapter ( HexBinaryAdapter . c l a s s ) @XmlSchemaType ( name = " hexBinary " ) protected byte [ ] t e s t H e x B i n a r y ; @XmlSchemaType ( name = " u n s i g n e d I n t " ) protected long t e s t U n s i g n e d I n t ; @XmlSchemaType ( name = " u n s i g n e d S h o r t " ) protected i n t t e s t U n s i g n e d S h o r t ; @XmlSchemaType ( name = " u n s i g n e d B y t e " ) protected short t e s t U n s i g n e d B y t e 2 ;
@XmlElement ( r e q u i r e d = true ) @XmlSchemaType ( name = " time " ) protected XMLGregorianCalendar t e s t T i m e ; @XmlElement ( r e q u i r e d = true ) @XmlSchemaType ( name = " d a t e " ) protected XMLGregorianCalendar t e s t D a t e ; @XmlElement ( r e q u i r e d = true ) @XmlSchemaType ( name = " anySimpleType " ) protected O b j e c t testAnySimpleType ; @XmlElement ( r e q u i r e d = true ) protected D u r a t i o n t e s t D u r a t i o n ; . s e t t e r é s g e t t e r metódusok . } A létrehozott Java osztályok közül most csak a ControllType és RowType egy-egy részlete került be a fenti programlistákba.A további generált Java forrásfájlok ezek: azért van, mert itt nem adtuk meg a minOccurs és maxOccurs paramétereket, tehát az előfordulás értéke 1. További érdekesség, hogy az @XmlType annotációnál létrejött a propOrder t is, ami felsorolásként tartalmazza azt a tag • DataType.java a dataType
complexType sorrendet, amit az érvényes XML-nek prezenrészére tálnia kell. Ez nem véletlen, hiszen ez az XSD xs:sequence részénél felsorolt elemeket jelenti. • SelectType.java a selectType complexType részére • ObjectFactory kényelmi osztály Figyeljük meg a RowType tanulmányozásával, hogy az egyes XML séma típusok milyen Java típusokra képződtek le. Az @XmlElement required = true attribútuma azt jelenti, hogy ez a mező kötelezően megjelenő az XML, ami 18 Dinamikus XML feldolgozás Gyakori feladat, hogy különféle XML szövegek érkeznek, amiket a típusuktól függően kell feldolgoznunk. Kommunikációs, integrációs környezetekben például egy sorba (Queue) érkezhetnek ezek az XML üzenetek, amiket más-más módon kell kezelnünk. Ebben a pontban azt mutatjuk Java programozói könyvtár meg, hogy az ilyen típusú feladatokat milyen módon tudjuk kezelni JAXB környezetben. Példaként tekintsük az 1-19 és 1-21 Programlistákat, amikhez
tartozó XSD-ket is láthatjuk (1-20. és 1-22. Programlisták) // 1 −19. P r o g r a m l i s t a : C l a s s F i r s t j a v a package o r g . c s j a x b t e s t ; import j a v a x . xml bind a n n o t a t i o n XmlElement ; import j a v a x . xml bind a n n o t a t i o n å XmlRootElement ; import j a v a x . xml bind a n n o t a t i o n XmlType ; @XmlRootElement ( namespace = " o r g . c s j a x b t e s t "å ) @XmlType ( namespace = " o r g . c s j a x b t e s t " ) public c l a s s C l a s s F i r s t { @XmlElement S t r i n g name ; } @XmlElement int value ; // 1 −20. P r o g r a m l i s t a : C l a s s F i r s t xsd <?xml version=" 1 . 0 " e n c o d i n g="UTF−8" å standalone=" y e s " ?> <x s : s c h e m a version=" 1 . 0 " t a r g e t N a m e s p a c e=" o r g å c s . j a x b t e s t " x m l n s : t n s=" o r g c s j a x b t e s t "å x m l n s : x
s=" h t t p : //www. w3 o r g / <x s : e l e m e n t name=" c l a s s F i r s t " t y p e="å t n s : c l a s s F i r s t "/> <xs:complexType name=" c l a s s F i r s t "> <x s : s e q u e n c e > <x s : e l e m e n t name="name" t y p e=" x s : s t r i n g "å minOccurs=" 0 "/> <x s : e l e m e n t name=" v a l u e " t y p e=" x s : i n t "/> </ x s : s e q u e n c e > </xs:complexType> </xs:schema > // 1 −21. P r o g r a m l i s t a : C l a s s S e c o n d j a v a package o r g . c s j a x b t e s t ; import j a v a x . xml bind a n n o t a t i o n XmlElement ; import j a v a x . xml bind a n n o t a t i o n å XmlRootElement ; import j a v a x . xml bind a n n o t a t i o n XmlType ; @XmlRootElement ( namespace = " o r g . c s j a x b t e s t "å ) @XmlType ( namespace = " o r g . c s j a x b t e s t " ) public c
l a s s C l a s s S e c o n d { @XmlElement String description ; JAXB - A Java és XML összekötése @XmlElement String t i t l e ; } @XmlElement int pages ; // 1 −22. P r o g r a m l i s t a : C l a s s S e c o n d xsd <?xml version=" 1 . 0 " e n c o d i n g="UTF−8" å standalone=" y e s " ?> <x s : s c h e m a version=" 1 . 0 " t a r g e t N a m e s p a c e=" o r g å c s . j a x b t e s t " x m l n s : t n s=" o r g c s j a x b t e s t "å x m l n s : x s=" h t t p : //www. w3 o r g / <x s : e l e m e n t name=" c l a s s S e c o n d " t y p e="å t n s : c l a s s S e c o n d "/> <xs:complexType name=" c l a s s S e c o n d "> <x s : s e q u e n c e > <x s : e l e m e n t name=" d e s c r i p t i o n " t y p e="å x s : s t r i n g " minOccurs=" 0 "/> <x s : e l e m e n t name=" t i t l e " t y p
e=" x s : s t r i n g å " minOccurs=" 0 "/> <x s : e l e m e n t name=" p a g e s " t y p e=" x s : i n t "/> </ x s : s e q u e n c e > </xs:complexType> </xs :sch ema > A GenXML osztály (1-23. Programlista) segítségével legyártottuk a ClassFirst.xml és ClassSecond.xml XML fájlokat, ezek lesznek a minta üzenetek, amiket különféle módon kell feldolgozni. A 15-22 sorok között létrehozzunk egyegy ClassFirst és ClassSecond objektumot A 27. sor nagyon fontos, mert ez egy olyan Class[] tömböt hoz létre, ami azokat az osztályokat sorolja fel, amiket majd a JAXB kontextusban kezelni, ismerni akarunk. A 29 sor ennek megfelelően hoz létre egy JAXBContext objektumot, amitől a következő sorban egy Marshaller objektumot igénylünk. A 32-33 sorokban mindkét előzetesen létrehozott objektumunkat XML fájlba perzisztáljuk. Most, hogy van 2 különböző XML üzenetünk, elérkeztünk oda, hogy be tudjuk
mutatni az azok egyedi kezelését megvalósító XMLProcessor osztályt (1-24. Programlista) Nézzük meg röviden a működését! A 14-23 sorok jelentése már bizonyára mindenkinek világos, ezért a 25. sorban lévő unmarshal() metódushívásra hívnánk fel először a figyelmet, ahol a beolvasott XML egy általános Object sta19 Java programozói könyvtár JAXB - A Java és XML összekötése tikus típusú objektumba kerül. A trükk igazából az instanceof utasításra épülő döntés már segít csak ennyi, mert utána az egyes elágazásokban a konkrét esetek szétválasztásában és feldolgozásában. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // 1 −23. P r o g r a m l i s t a : GenXML j a v a package o r g . c s j a x b t e s t ; import import import import import java . io F i l e ; j a v a x . xml bind JAXBContext ; j a v a x . xml
bind JAXBException ; j a v a x . xml bind M a r s h a l l e r ; t e s t . c e g o r g Employee ; public c l a s s GenXML { public s t a t i c void main ( S t r i n g [ ] a r g s ) throws JAXBException { C l a s s F i r s t c f = new C l a s s F i r s t ( ) ; c f . name = " N y i r i ␣ Imre " ; c f . value = 5; C l a s s S e c o n d c s = new C l a s s S e c o n d ( ) ; c s . d e s c r i p t i o n = "Ez␣ egy ␣ j ó ␣ l e c k e ␣ v o l t " ; cs . pages = 100; cs . t i t l e = " Informatikai ␣ Navigátor " ; F i l e x m l f i l e 1 = new F i l e ( " /home/ t a n u l a s /xml/ C l a s s F i r s t . xml" ) ; F i l e x m l f i l e 2 = new F i l e ( " /home/ t a n u l a s /xml/ C l a s s S e c o n d . xml" ) ; Class [ ] } } c l a s s e s = new C l a s s [ ] { C l a s s F i r s t . c l a s s , C l a s s S e c o n d c l a s s } ; JAXBContext j a x b C o n t e x t = JAXBContext . n e w I n s t a n c e ( c l a s s e s ) ;
Marshaller jaxbMarshaller = jaxbContext . c r e a t e M a r s h a l l e r ( ) ; j a x b M a r s h a l l e r . s e t P r o p e r t y ( M a r s h a l l e r JAXB FORMATTED OUTPUT, Boolean TRUE) ; j a x b M a r s h a l l e r . marshal ( c f , x m l f i l e 1 ) ; j a x b M a r s h a l l e r . marshal ( cs , x m l f i l e 2 ) ; // 1 −24. P r o g r a m l i s t a : XMLProcessor j a v a package o r g . c s j a x b t e s t ; import import import import java . io F i l e ; j a v a x . xml bind JAXBContext ; j a v a x . xml bind JAXBException ; j a v a x . xml bind U n m a r s h a l l e r ; public c l a s s XMLProcessor { public s t a t i c void main ( S t r i n g [ ] a r g s ) throws JAXBException { C l a s s [ ] c l a s s e s = new C l a s s [ ] { org . cs jaxb t e s t C l a s s F i r s t class , org . cs jaxb t e s t ClassSecond class }; JAXBContext j a x b C o n t e x t = JAXBContext . n e w I n s t a n c e ( c l a s s e s ) ; Unmarshaller jaxbUnmarshaller = jaxbContext
. createUnmarshaller ( ) ; F i l e x m l f i l e = new F i l e ( " /home/ t a n u l a s /xml/ C l a s s S e c o n d . xml" ) ; 20 Java programozói könyvtár 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 JAXB - A Java és XML összekötése O b j e c t xmlObject = j a x b U n m a r s h a l l e r . unmarshal ( x m l f i l e ) ; } } i f ( xmlObject instanceof C l a s s F i r s t ) { C l a s s F i r s t c f = ( C l a s s F i r s t ) xmlObject ; System . out p r i n t l n ( c f name ) ; System . out p r i n t l n ( c f v a l u e ) ; } e l s e i f ( xmlObject instanceof C l a s s S e c o n d ) { C l a s s S e c o n d c f = ( C l a s s S e c o n d ) xmlObject ; System . out p r i n t l n ( c f d e s c r i p t i o n ) ; System . out p r i n t l n ( c f p a g e s ) ; System . out p r i n t l n ( c f t i t l e ) ; } else { System . out p r i n t l n ( " I s m e r e t l e n
␣XML! " ) ; } Érdekes XML séma lehetőségek Ismétlődések és XML tag számosságok Ebben a pontban az 1-16. Programlista sémáját néhány helyen megváltoztatjuk és tanulmányozzuk, hogy mindez milyen hatással van a generált Java osztályokra. Ez a módszer azért lesz tanulságos, mert azután mi is tudunk így annotált class-okat készíteni, azaz sokat tanulhatunk a JAXB „@”-okról. Eddig nem mutattuk meg, hogy milyen lett a generált DataType class, pedig lehet belőle tanulni, emiatt nézzük is meg! Amit meg kell figyelnünk, az a protected List<RowType> row sor (45. sor), ugyanis a row tag 0 vagy több ismétlődéssel lehet a séma szerint, amit ez a konstrukció jól leképez. // 1 −25. P r o g r a m l i s t a : DataType j a v a // // This f i l e was g e n e r a t e d by t h e JavaTM A r c h i t e c t u r e f o r XML B i n d i n g (JAXB) R e f e r e n c e å Implementation , v2 .24 −2 // See <a h r e f =" h t t p : / / j a v a .
sun com/ xml / j a x b "> h t t p : / / j a v a sun com/ xml / j a x b </a> // Any m o d i f i c a t i o n s t o t h i s f i l e w i l l be l o s t upon r e c o m p i l a t i o n o f t h e s o u r c e schema . // Generated on : 2 0 1 3 . 0 4 0 6 a t 0 4 : 2 2 : 1 6 PM GMT // package c s . o r g j a x b t e s t ; import import import import import java . u t i l ArrayList ; java . u t i l List ; j a v a x . xml bind a n n o t a t i o n XmlAccessType ; j a v a x . xml bind a n n o t a t i o n XmlAccessorType ; j a v a x . xml bind a n n o t a t i o n XmlType ; /∗ ∗ ∗ <p>Java c l a s s f o r dataType complex t y p e . ∗ ∗ <p>The f o l l o w i n g schema f r a g m e n t s p e c i f i e s t h e e x p e c t e d c o n t e n t c o n t a i n e d w i t h i n t h i s c l a s s . ∗ ∗ <pre> 21 Java programozói könyvtár 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 JAXB - A Java és XML
összekötése ∗ & l t ; complexType name="dataType"> ∗ & l t ; complexContent> ∗ & l t ; r e s t r i c t i o n b a s e ="{ h t t p : / /www. w3 o r g /2001/XMLSchema} anyType"> ∗ & l t ; s e q u e n c e maxOccurs="unbounded " minOccurs="0"> ∗ & l t ; e l e m e n t name="row" t y p e ="{urn : j a x b t e s t / x s d 2 j a v a }rowType"/> ∗ &l t ; / sequence> ∗ &l t ;/ r e s t r i c t i o n > ∗ & l t ; / complexContent> ∗ & l t ; / complexType> ∗ </pre> ∗ ∗ ∗/ @XmlAccessorType ( XmlAccessType . FIELD) @XmlType ( name = " dataType " , propOrder = { " row " }) public c l a s s DataType { protected L i s t <RowType> row ; } public L i s t <RowType> getRow ( ) { i f ( row == n u l l ) { row = new A r r a y L i s t <RowType>() ; } return t h i s . row ; } Mi történik, ha a maxOccurs részt
töröljük és Az erre generált DataType class pedig ismét csak a minOccurs=”0” deklarációt hagyjuk meg? egy új elemmel fog bővülni, azaz a row adatEkkor a generált változó – ahogy vártuk is – nem tagnál megjelenik a required = true jelzés, ami tömb lesz (protected RowType row ): azért van, mert a row előfordulása most pontosan 1, azaz ebben az értelemben egy kötelezően @XmlAccessorType ( XmlAccessType . FIELD) @XmlType ( name = " dataType " , propOrder = { előforduló része az XML-nek: " row " }) public c l a s s DataType { protected RowType row ; } public RowType getRow ( ) { return row ; } @XmlElement ( r e q u i r e d = true ) protected RowType row ; public void setRow ( RowType v a l u e ) { t h i s . row = v a l u e ; } public RowType getRow ( ) { return row ; } Amennyiben a minOccurs részt is elhagyjuk, akkor a dataType sémarészlet így fog kinézni: <xs : complexType name=" dataType "> <xs : s e q u
e n c e > <xs : e l e m e n t t y p e=" s c h : rowType" name=" row "å xmlns : s c h=" urn : j a x b t e s t / x s d 2 j a v a "/> </xs : s e q u e n c e > </xs : complexType> 22 @XmlAccessorType ( XmlAccessType . FIELD) @XmlType ( name = " dataType " , propOrder = { " row " }) public c l a s s DataType { } public void setRow ( RowType v a l u e ) { t h i s . row = v a l u e ; } Elem sorrend Egy nyelvtan lényeges része, hogy a szavai milyen sorrendben következhetnek egymás után. Java programozói könyvtár JAXB - A Java és XML összekötése Az XML-ben is lényeges, hogy az egyes címkék- sorrendről: nek mi a sorrendje, ezért az XML séma ezzel a . kérdéskörrel részletesen foglalkozik. Nézzük pél- @XmlAccessorType ( XmlAccessType FIELD) @XmlType ( name = " s e l e c t T y p e " , propOrder dául a selectType séma típust: . <xs : complexType name=" s e l e c t T y p
e "> <xs : s e q u e n c e > <xs : e l e m e n t t y p e=" s c h : c o n t r o l l T y p e " nameå =" c o n t r o l l " xmlns : s c h=" urn : j a x b t e s t /å x s d 2 j a v a "/> <xs : e l e m e n t t y p e=" s c h : dataType " name="å data " xmlns : s c h=" urn : j a x b t e s t /å x s d 2 j a v a "/> </xs : s e q u e n c e > </xs : complexType> . = { }) public c l a s s S e l e c t T y p e { . @XmlElement ( r e q u i r e d = true ) protected C o n t r o l l T y p e c o n t r o l l ; @XmlElement ( r e q u i r e d = true ) protected DataType data ; Elem attribútum Itt az xs:sequence egy szigorú sorrendet ír le, Legyen az a feladatunk, hogy a selectType elemazaz a controll és data címkéknek a felsorolás nek egy lang nevű attribútumot is tervezünk, sorrendjében kell az XML-ben is előfordulniuk. ekkor a séma így módosul: Ennek megfelelően a generált Java class is
ezt . írja elő, ami a propOrder résznél való felsorolás- <xs : complexType name=" s e l e c t T y p e "> <xs : s e q u e n c e > ban tükröződik vissza. <xs : e l e m e n t t y p e=" s c h : c o n t r o l l T y p e " nameå . @XmlAccessorType ( XmlAccessType . FIELD) @XmlType ( name = " s e l e c t T y p e " , propOrder = { " controll " , " data " }) public c l a s s S e l e c t T y p e { . } @XmlElement ( r e q u i r e d = true ) protected C o n t r o l l T y p e c o n t r o l l ; @XmlElement ( r e q u i r e d = true ) protected DataType data ; Amennyiben az xs:sequence helyett xs:all megadást adunk a sémában, akkor ez azt jelenti, hogy ezeknek az elemeknek továbbra is elő kell fordulniuk, de most már mindegy a sorrendjük: . <xs : complexType name=" s e l e c t T y p e "> <xs : a l l > <xs : e l e m e n t t y p e=" s c h : c o n t r o l l T y p e " nameå =" c
o n t r o l l " xmlns : s c h=" urn : j a x b t e s t /å x s d 2 j a v a "/> <xs : e l e m e n t t y p e=" s c h : dataType " name="å data " xmlns : s c h=" urn : j a x b t e s t /å x s d 2 j a v a "/> </xs : a l l > </xs : complexType> . =" c o n t r o l l " xmlns : s c h=" urn : j a x b t e s t /å x s d 2 j a v a "/> <xs : e l e m e n t t y p e=" s c h : dataType " name="å data " xmlns : s c h=" urn : j a x b t e s t /å x s d 2 j a v a "/> </xs : s e q u e n c e > <xs : a t t r i b u t e name=" l a n g " t y p e=" xs : s t r i n g "/> </xs : complexType> . A lefordított Java kód pedig így fogja ezt JAXB annotáció szinten tükrözni: . @XmlAccessorType ( XmlAccessType . FIELD) @XmlType ( name = " s e l e c t T y p e " , propOrder = { " controll " , " data " }) public c l a s s S e l
e c t T y p e { . @XmlElement ( r e q u i r e d = true ) protected C o n t r o l l T y p e c o n t r o l l ; @XmlElement ( r e q u i r e d = true ) protected DataType data ; @XmlAttribute ( name = " l a n g " ) protected S t r i n g l a n g ; A simpleType kódja Ez a generált Java osztályon is korrekt mó- Egészítsük ki a selectType-ot egy simpleType tídon látszik, azaz a propOrder nem rendelkezik a pussal. A sémarészlet így változik: 23 Java programozói könyvtár . . <xs : complexType name=" s e l e c t T y p e "> <xs : s e q u e n c e > <xs : e l e m e n t t y p e=" s c h : c o n t r o l l T y p e å " name=" c o n t r o l l " xmlns : s c h="å urn : j a x b t e s t / x s d 2 j a v a "/> <xs : e l e m e n t t y p e=" s c h : dataType " å name=" data " xmlns : s c h=" urn : å j a x b t e s t / x s d 2 j a v a "/> <xs : e l e m e n t t y p e=" s c h :
NumberType" å name="number" xmlns : s c h=" urn : å j a x b t e s t / x s d 2 j a v a "/> </xs : s e q u e n c e > <xs : a t t r i b u t e name=" l a n g " t y p e=" xs : å s t r i n g "/> </xs : complexType> <xs : simpleType name="NumberType"> <xs : r e s t r i c t i o n b a s e=" xs : s t r i n g "> <xs : p a t t e r n v a l u e="d {5} " /> </xs : r e s t r i c t i o n > </xs : simpleType> A létrehozott Java kód: . @XmlAccessorType ( XmlAccessType . FIELD) @XmlType ( name = " s e l e c t T y p e " , propOrder = { " controll " , " data " , "number" }) public c l a s s S e l e c t T y p e { . @XmlElement ( r e q u i r e d = true ) protected C o n t r o l l T y p e c o n t r o l l ; @XmlElement ( r e q u i r e d = true ) protected DataType data ; @XmlElement ( r e q u i r e d = true ) protected S t r i n g number ;
@XmlAttribute ( name = " l a n g " ) protected S t r i n g l a n g ; Az XML séma egy elég kiterjedt szabvány, ezért most a kísérleteket abbahagyjuk, de ezzel a módszerrel bármilyen XSD konstrukcióról fel lehet deríteni, hogy milyen JAXB annotált osztály tartozik hozzá. JAXB - A Java és XML összekötése JAXB alapfeladata, hogy bármely Java osztály objektumát XML szöveggé alakítson (szerializáció), illetve fordítva, bármely XML szövegből egy őt reprezentálni képes objektumot állítson elő (deszerializáció). Ehhez alapvetően a javax.xmlbindJAXBElement class használatos, ami képes bármilyen Java objektumot becsomagolni A szerializálandó osztályt pedig a javax.xmlbindannotationXmlRootElement annotációval kell megjelölni Annotáció nélkül. Alaphasználat során nem is kell „@”-okat elhelyezni az osztályra, nézzük ugyanis az ismert 2 osztályunkat ebben a formában! Láthatjuk, hogy most nincs semmilyen jelölés sem az
Address, sem az Employee osztályon. Ugyanakkor fontos megjegyezni, hogy az objektumok állapotát mindig a publikus jellemzőik (adattagjaik) jelentik, így az szerializációba bevonni kívánt adatmezőknek vagy publikusnak kell lenniük, vagy biztosítani kell a megfelelő getter metódusokat. Ez lesz a mostani példában használt Employee: // Employee . j a v a "@" n é l k ü l package o r g . c s t e s t ; public c l a s s Employee { Address a d d r e s s ; S t r i n g name ; String sex ; int kor ; double s u l y ; public Address g e t A d d r e s s ( ) { return a d d r e s s ; } A JAXB annotációk áttekintése A most következő pontban rendszerezetten áttekintjük a JAXB annotációkat. Az előzőekben inkább az XML séma oldaláról kísérletezve derítettük ki ezeket a lehetőségeket, most pedig a dokumentáció alapján fogjuk áttekinteni, kiegészíteni a már eddig megismert tudást. A 24 . } public void s e t A d d r e s s ( Address a d d r e s s )
{ this . address = address ; } És a beágyazott Address class: Java programozói könyvtár // Address . j a v a "@" n é l k ü l package o r g . c s t e s t ; public c l a s s Address { String street ; i n t houseNumber ; public S t r i n g g e t S t r e e t ( ) { return s t r e e t ; } . } public void s e t S t r e e t ( S t r i n g s t r e e t ) { this . s t r e e t = s t r e e t ; } Az Employee egy példányának XML szerializációját mutatja a 1-26. Programlista Nincs annotáció, de mégis tudunk olyan kódot írni, ami a 26-32 sorok közötti e Employee objektumot XML-be alakítja. A 34-36 sorok más ismerősek A 38 sor jelenti az újdonságot, ahol lét1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 JAXB - A Java és XML összekötése rehozunk egy Employee class-ra vonatkozó JAXBElement példányt, ahol a konstruktor ezeket a paramétereket kapja: • A gyökérelem neve az XML eredményben. Ezt egy
minősített QName osztály objektum segítségével adhattuk meg. • A szerializálandó class neve (Employee) • A konkrét szerializálandó objektum neve (e) A további sorok már egyértelműek, a 41. sorban a képernyőre ki is íratjuk a kapott XML-t Megjegyezzük, hogy ez a módszer azért hasznos, mert általában nincsenek a Java class-ok JAXB szerint annotálva, de mégis gyorsan hozzájuthatunk az objektum egy XML reprezentációjához, ami sok helyen hasznos lehet. // 1 −26. P r o g r a m l i s t a : TestNoAnnotation j a v a package o r g . c s t e s t ; import import import import import import import import import java . io F i l e ; java . io StringReader ; java . io StringWriter ; j a v a x . xml bind JAXBContext ; j a v a x . xml bind JAXBElement ; j a v a x . xml bind JAXBException ; j a v a x . xml bind M a r s h a l l e r ; j a v a x . xml bind U n m a r s h a l l e r ; j a v a x . xml namespace QName ; public c l a s s TestNoAnnotation {
public s t a t i c void main ( S t r i n g [ ] a r g s ) { System . out p r i n t l n ( " S t a r t " ) ; try { Address a = new Address ( ) ; a . houseNumber = 1 2 ; a . s t r e e t = " Váci ␣ út " ; Employee e = new Employee ( ) ; e . address = a ; e . kor = 3 2 ; e . name = "Alma␣ F e r e n c " ; e . sex = " f é r f i " ; e . suly = 88; JAXBContext j c = JAXBContext . n e w I n s t a n c e ( o r g c s t e s t Employee c l a s s ) ; 25 Java programozói könyvtár 35 36 37 38 39 40 41 42 43 44 45 46 47 JAXB - A Java és XML összekötése Marshaller jaxbMarshaller = jc . createMarshaller () ; j a x b M a r s h a l l e r . s e t P r o p e r t y ( M a r s h a l l e r JAXB FORMATTED OUTPUT, Boolean TRUE) ; } } JAXBElement<Employee> r o o t = new JAXBElement<Employee >(new QName( " employee " ) , å Employee . c l a s s , e ) ; S t r i n g W r i t e r sw = new S t r i n g W r i t e r ( ) ; j a x b M a
r s h a l l e r . marshal ( r o o t , sw ) ; System . out p r i n t l n ( sw ) ; } catch ( JAXBException e ) { e . printStackTrace () ; } JAXB annotációk Az annotációkat általában a class vagy az adattag szintjén adhatjuk meg és ezzel teljes rugalmassággal adhatjuk meg, hogy milyen XML outputot szeretnénk előállítani. Ezek a jelölések a javax.xmlbindannotation csomagban kaptak helyet a Java SDK-ban. A rövid ismertetésben – ahogy eddig is – a Java Bean property és az adattag ugyanazt jelenti. • @XmlRootElement: Amennyiben egy osztályt XML szerializálni szeretnénk, úgy a class deklaráció elé kell tenni, utalva arra, hogy ez az az osztály, amit XML szöveggé akarunk alakítani. • @XmlElement: Egy XML tag és egy Java property összerendelése. • @XmlAttribute: Amennyiben egy Java class adattagját ezzel annotálunk, úgy ezzel azt mondjuk meg, hogy ez egy XML attribútum értékét fogja tárolni, azaz azzal lesz mappelve. Az XML property – ahogy
azt bizonyára sokan tudják – a címke egy jellemzője, azaz nem a nyitó és záró tag közötti érték. • @XmlType: Amennyiben egy osztályt ezzel jelölünk meg, akkor az az XML sémában egy complex típus lesz. A példáinkban láthattuk a használatát. Kiemeljük, hogy 26 például az XML címkék sorrendjét is ezen keresztül tudtuk megadni. • @XmlID: Kizárólag csak egy String típusú adattag jelölhető meg ezzel, ami kulcsként fog szerepelni. Az @XmlIDREF segítségével hivatkozhatunk rá • @XmlIDREF : Kizárólag csak adattag jelölhető meg ezzel, amely tag maga is egy osztály, amit a szerializálás része. Amennyiben egy @XmlID azonosítóra hivatkozunk itt, akkor nem az egész típus, hanem csak a kulcsként kijelölt mező fog szerializálódni. • @XmlTransient: Ezek egy explicite módon való deklarációt tudunk megadni egy bean property-re vonatkozólag, hogy azt nem szeretnénk az előállított XML szövegben látni. • @XmlValue: Ez is csak
adattagra adható meg és egy XML elem tartalmat reprezentál. • @XmlJavaTypeAdapter : Megadhatunk egy Java class-t, ami pontosan megvalósít egy Java↔XML leképzést. Használatára a későbbiekben egy részletes példát is mutatunk. • @XmlElementWrapper : Mindenütt használható, ahol a @XmlElement is. Hasz- Java programozói könyvtár nálatára külön példát mutatunk. JAXB - A Java és XML összekötése public c l a s s MyPoint { public i n t x ; public i n t y ; • @XmlAccessorType: A legfelsőbb szintű osztályra annotálhatjuk ezt a lehetőséget, @XmlID amivel azt lehet szabályozni, hogy általápublic S t r i n g key ; ban milyen osztály adattagok kerüljenek a } szerializációba. Alapértelmezésben a pubLáthatjuk, hogy a String típusú és key nevű lic tagok szerializálva lesznek. Viszonylag adatmezőt kulcsnak jelöltük A szerializálandó ritkán használjuk, de az xjc általában min- osztály pedig legyen ez: dig legenerálja ezt az
annotációt is. // MyPoint . c l a s s Az adatmodell Az annotációk segítségével nagy pontossággal tudjuk az XML és Java szerkezeteket, neveket (és névtereket) összerendelni (binding). Amikor egy Java class-t vagy XML sémát tervezünk, akkor az egy adatmodellezés. Az új elem az, hogy itt egyszerre 2 modellező nyelvben is gondolkodunk közben: • XML séma (XML) • Java osztályok és a közöttük lévő kapcsolatok (Java) Ugyanakkor a 2 modell között tudatosan nem képezünk le mindent a másikra. Az @XmlTransient például pont azt jelentette, hogy az így megcímkézett adatmező nem része az XML séma alapú modellnek. Az @XmlID és @XmlIDREF használatára egy konkrét példa lehet egy olyan MyPoint class, aminek a következő 3 adattagja van: // MyPoint . c l a s s package o r g . c s t e s t ; @XmlRootElement 1 2 3 4 5 6 7 8 package o r g . c s t e s t ; @XmlRootElement public c l a s s Pontok { @XmlElement public MyPoint pA ; } @XmlIDREF public MyPoint
pB ; Ekkor a pA pont teljes egészében szerializálódik az output XML-ben, azonban a pB pontnak csak a key adattagja, mert ott ezt kértük, azaz elég a kulcs értéke az XML szövegbe. A @XmlElementWrapper A gyűjteményeknél fontos kérdés, hogy az elemeket csak magukban tüntetjük-e fel, vagy egy összefogó külső XML tag is utal az egész adatszerkezetre. Ennek könnyebb megértésére egy kis példa szolgálhat. Nézzük a szerializálandó ClassThird osztályt (1-27. Programlista), ahol a @XmlElementWrapper egyelőre megjegyzésbe került. A GenThirdXML (1-28 Programlista) osztályt futtassuk le, ez legyártja a szerializált XML-t, amit a 1-29. Programlistán tekinthetünk meg // 1 −27. P r o g r a m l i s t a : C l a s s T h i r d j a v a package o r g . c s j a x b t e s t ; import import import import j a v a x . xml bind a n n o t a t i o n XmlElement ; j a v a x . xml bind a n n o t a t i o n XmlElementWrapper ; j a v a x . xml bind a n n o t a t i
o n XmlRootElement ; j a v a x . xml bind a n n o t a t i o n XmlType ; 27 Java programozói könyvtár 9 10 11 12 13 14 15 16 17 18 19 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 JAXB - A Java és XML összekötése @XmlRootElement ( namespace = " o r g . c s j a x b t e s t " ) @XmlType ( namespace = " o r g . c s j a x b t e s t " ) public c l a s s C l a s s T h i r d { @XmlElement S t r i n g name ; } //@XmlElementWrapper ( name=" k a t e g o r i a k ") @XmlElement int [ ] value ; // 1 −28. P r o g r a m l i s t a : GenThirdXML j a v a package o r g . c s j a x b t e s t ; import import import import java . io F i l e ; j a v a x . xml bind JAXBContext ; j a v a x . xml bind JAXBException ; j a v a x . xml bind M a r s h a l l e r ; public c l a s s GenThirdXML { public s t a t i c void main ( S t r i n g [ ] a r g s ) throws JAXBException { C l a s s T h i r d c t = new C l a
s s T h i r d ( ) ; c t . name = " K o r c s o p o r t o k " ; ct ct ct ct . value . value . value . value = new i n t [ 3 ] ; [ 0 ] = 12; [ 1 ] = 16; [ 2 ] = 18; F i l e x m l F i l e = new F i l e ( " /home/ t a n u l a s /xml/ C l a s s T h i r d . xml" ) ; Class [ ] } } c l a s s e s = new C l a s s [ ] { C l a s s T h i r d . c l a s s } ; JAXBContext j a x b C o n t e x t = JAXBContext . n e w I n s t a n c e ( c l a s s e s ) ; Marshaller jaxbMarshaller = jaxbContext . c r e a t e M a r s h a l l e r ( ) ; j a x b M a r s h a l l e r . s e t P r o p e r t y ( M a r s h a l l e r JAXB FORMATTED OUTPUT, Boolean TRUE) ; j a x b M a r s h a l l e r . marshal ( ct , x m l F i l e ) ; Ezen az eredmény XML szövegen semmi <name>K o r c s o p o r t o k </name> <v a l u e >12</v a l u e > meglepő nincs, mindent az eddig is ismert esz<v a l u e >16</v a l u e > <v a l u e >18</v a l u e > közökkel csináltunk.
Az egyedüli jelenség, amit </ns2 : c l a s s T h i r d > érdemes észrevenni az, hogy az int[] value adattag a value XML tag-ek sorozatára képződött le. Sok esetben praktikus vagy csak egyszerűen Természetesen ezt nem csak tömbre, hanem lis- áttekinthetőbb, ha ezeket a value tag-eket egy tára vagy bármely más kollekció típusra is így őket befogadó XML címkébe ágyazzuk, ahogy használhatjuk. azt az 1-30. Programlistán észrevehető kategoriak címke is teszi Ezt úgy tudtuk meg// 1 −29 P r o g r a m l i s t a valósítani, hogy a ClassThird class 17. sorá<?xml v e r s i o n=" 1 0 " e n c o d i n g="UTF−8" å ból kivesszük a megjegyzést és hagyjuk műs t a n d a l o n e=" y e s "?> <ns2 : c l a s s T h i r d xmlns : ns2=" o r g . c s j a x b t e s t "> ködni a @XmlElementWrapper annotációt a va28 Java programozói könyvtár lue property-re, ahol azt is specifikáltuk, hogy a becsomagoló
XML tag neve kategoriak legyen. JAXB - A Java és XML összekötése { // 1 −30. P r o g r a m l i s t a <?xml v e r s i o n=" 1 . 0 " e n c o d i n g="UTF−8" å s t a n d a l o n e=" y e s "?> <ns2 : c l a s s T h i r d xmlns : ns2=" o r g . c s j a x b t e s t "> <name>K o r c s o p o r t o k </name> <k a t e g o r i a k > <v a l u e >12</v a l u e > <v a l u e >16</v a l u e > <v a l u e >18</v a l u e > </ k a t e g o r i a k > </ns2 : c l a s s T h i r d > @XmlElement ( namespace = " o r g . c e g t e s t " ) public S t r i n g name ; @XmlElement public S t r i n g s e x ; @XmlElement public i n t k o r ; } Adapter készítése Korábbi programozási tapasztalataink alapján tudjuk, hogy adaptert mindig olyankor készítünk, amikor valamilyen 2 dolog nem illik össze, de azt mégis együtt szeretnénk használni. Erről többet is olvashatunk
az Informatikai Navigátor 7. számának 10 cikkéből Tekintsük a következő Address class változatot, aminek a neve AddressWithMap. Itt a cím adatelemeit egy Map adatszerkezetben tároljuk: // AddressWithMap . j a v a package c s . o r g j a x b t e s t a d a p t e r ; import j a v a . u t i l HashMap ; import j a v a . u t i l Map ; public c l a s s AddressWithMap { // k e y s : s t r e e t , houseNumber public Map addressMap = new HashMap ( ) ; } Ennek megfelelően az Employee class helyett is van egy másik hasonló, az EmployeeOther : // EmployeeOther . j a v a package c s . o r g j a x b t e s t a d a p t e r ; import j a v a x . xml bind a n n o t a t i o n XmlElement ; import j a v a x . xml bind a n n o t a t i o n å XmlRootElement ; import j a v a x . xml bind a n n o t a t i o n XmlType ; import j a v a x . xml bind a n n o t a t i o n a d a p t e r s å XmlJavaTypeAdapter ; @XmlRootElement ( namespace = " o r g . c e g t e s t " )
@XmlType ( namespace = " o r g . c e g t e s t " ) public c l a s s EmployeeOther @XmlElement @XmlJavaTypeAdapter ( v a l u e=AddressAdapter . å class ) public AddressWithMap a d d r e s s ; @XmlElement public double s u l y ; Vegyük észre, hogy most az address adattagot egy Map-ben tároljuk, aminek nincs alapértelmezett XML szerializációja, pedig mi azt szeretnénk. Sőt az is jó lenne, ha kompatibilis maradna az eddigi, korábbi XML szerkezettel A megoldás az, hogy az AddressWithMap típusú address jellemzőre definiálunk egy adapter funkciót betöltő Java class-t, amit a @XmlJavaTypeAdapter jelöléssel adhatunk meg és a value attribútum értéke maga az adapter osztály neve, ami esetünkben AddressAdapter lesz. Ennek a mágikus osztálynak azt a feladatot fogjuk adni, hogy minden olyan értéket, ami egy AddressWithMap osztályból származik (esetünkben az EmployeeOther class address tagja) képezzen le a már jól ismert Address osztályunkra, majd
bízzuk arra a szerializálást. Nézzük meg az Address osztályt újra: // Address . j a v a package c s . o r g j a x b t e s t a d a p t e r ; import j a v a x . xml bind a n n o t a t i o n XmlElement ; import j a v a x . xml bind a n n o t a t i o n XmlType ; @XmlType ( namespace = " o r g . c e g t e s t " ) public c l a s s Address { @XmlElement public S t r i n g s t r e e t ; } @XmlElement public i n t houseNumber ; 29 Java programozói könyvtár Ennyi előzmény után már biztos mindenki kíváncsi magára az adapterre, az AddressAdapter class-ra (1-31. Programlista) Ez egy olyan XmlAdapter ősosztályt kiterjesztő osztály, aminek 2 metódusa van: • marshal() Leképezi a szerializálandó objektumot arra az objektumra, amit már tudunk szerializálni • unmarshal() Az XML-ből visszaolvasásra képes objektumból legyártja azt az objektumot, amit használunk, azaz a deszerializációját visszavezettük az első objektumra. 1 2 3 4 5 6 7
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 1 2 3 4 5 6 7 8 9 10 11 12 JAXB - A Java és XML összekötése Látható, hogy az öröklésnél mindig konkretizálni kell (AddressAdapter.java, 7 sor) az éppen használt un BoundType és ValueType osztályokat, amiket meg is tettünk ezen 2 osztály használatával: Address, AddressWithMap. A marshal() és unmarshal() kódja egyértelmű és egyszerű, megadja az AddressWithMap ↔ Address oda-vissza konvertálás algoritmusát. Az 132 Programlista bemutatja, hogy ezek után egy EmployeeOther objektumot milyen módon szerializálhatunk XML-be. Semmi különleges nincs benne, csak a teljesség kedvéért mutatjuk meg. A futtatás során előállított EmployeeOther.xml fájlt (1-33. Programlista) megtekintve látható, hogy a kívánt eredményt kaptuk, az adapter a tervnek megfelelően működött. // 1 −31. P r o g r a m l i s t a : AddressAdapter j a v a package c s . o r g j a x b t e s t a d a p t e r ; import j a v
a x . xml bind a n n o t a t i o n a d a p t e r s XmlAdapter ; public c l a s s AddressAdapter extends XmlAdapter<Address , AddressWithMap> { @Override public AddressWithMap unmarshal ( Address v t ) throws E x c e p t i o n { AddressWithMap am = new AddressWithMap ( ) ; am . addressMap put ( " s t r e e t " , vt s t r e e t ) ; am . addressMap put ( " houseNumber " , v t houseNumber ) ; return am ; } } @Override public Address marshal ( AddressWithMap bt ) throws E x c e p t i o n { Address a = new Address ( ) ; a . s t r e e t = ( S t r i n g ) bt addressMap g e t ( " s t r e e t " ) ; a . houseNumber = I n t e g e r p a r s e I n t ( ( S t r i n g ) bt addressMap g e t ( " houseNumber " ) ) ; return a ; } // 1 −32. P r o g r a m l i s t a : TestJAXBAdapter j a v a package c s . o r g j a x b t e s t a d a p t e r ; import import import import java . io F i l e ; j a v a x . xml bind JAXBContext ; j a v a x .
xml bind JAXBException ; j a v a x . xml bind M a r s h a l l e r ; public c l a s s TestJAXBAdapter { public s t a t i c void main ( S t r i n g [ ] a r g s ) throws JAXBException 30 Java programozói könyvtár 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 { JAXB - A Java és XML összekötése AddressWithMap a = new AddressWithMap ( ) ; a . addressMap put ( " s t r e e t " , " Budapest , ␣ Görgey ␣ Artúr ␣ u t c a " ) ; a . addressMap put ( " houseNumber " , " 102 " ) ; EmployeeOther e = new EmployeeOther ( ) ; e . address = a ; e . kor = 3 2 ; e . name = "Alma␣ F e r e n c " ; e . sex = " f é r f i " ; e . suly = 88; } } JAXBContext j a x b C o n t e x t = JAXBContext . n e w I n s t a n c e ( c s o r g j a x b t e s t a d a p t e r EmployeeOtherå . class ) ; Marshaller jaxbMarshaller = jaxbContext . c r e a t e M a r s h a l l e r ( ) ; j a x b M a r s h a l l e r . s e t P r
o p e r t y ( M a r s h a l l e r JAXB FORMATTED OUTPUT, Boolean TRUE) ; F i l e XMLfile = new F i l e ( " /home/ t a n u l a s /xml/ EmployeeOther . xml" ) ; j a x b M a r s h a l l e r . marshal ( e , XMLfile ) ; // 1 −33. P r o g r a m l i s t a : EmployeeOther xml <?xml v e r s i o n=" 1 . 0 " e n c o d i n g="UTF−8" s t a n d a l o n e=" y e s "?> <ns2 : employeeOther xmlns : ns2=" o r g . c e g t e s t "> <a d d r e s s > <s t r e e t >Budapest , Görgey Artúr utca </ s t r e e t > <houseNumber >102</houseNumber> </a d d r e s s > <ns2 : name>Alma Ferenc </ns2 : name> <sex>f é r f i </sex> <kor >32</kor> <s u l y >88.0</ s u l y > </ns2 : employeeOther> JAXB alapú séma validáció XML validáció alatt azt értjük, hogy van egy XML szöveg és egy XML séma (vagy bármely más módon megadott grammatika), aminek az
előírásaihoz való illeszkedését vizsgáljuk a kérdéses XML szövegnek. A JAXB alapú séma validálás módját az Informatikai Navigátor 4 számának 92 pontjánál már ismertettük, akinek ez szükséges, ott átnézheti. Összefoglalás A JAXB API a Java 6 óta része az alap SDKnak, ezért mindenütt érdemes használni, ahol fontos az XML sémára épülő precíz XML ke- zelés. Az is előnye ennek az eszközkészletnek, hogy a programozó képes Java objektumokkal kezelni az XML adatstruktúrákat, ami sokszor sokkal áttekinthetőbb, mint a SAX vagy DOM alapú feldolgozás. A JAXB alapszintű megértése azért is fontos, mert a korszerű Java webservice architektúra (JAX-WS ) a Java és XML binding-hoz természetes módon ezt használja és bármikor szükségünk lehet a szerializációt kicsi vagy ritkábban akár nagyobb mértékben is testre szabni. Befejezésül fontosnak tartjuk megemlíteni, hogy több éve rendelkezésre áll egy másik API is, az XMLBeans
(http: //xmlbeans.apacheorg/), ami jóval megelőzte időben a JAXB megoldásait, de képesség tekintetében szintén tudja a fenti szolgáltatásokat is. 31 Java programozói könyvtár 2. BeanShell - Könnyűsúlyú scriptek Java nyelvbe ágyazva BeanShell - Könnyűsúlyú scriptek Java nyelvbe ágyazva A fordított (compiler ) és értelmezett (interpreter ) környezetek mindig egy kettősséget jelentettek a számítógépes nyelvek világában. Mindkét megközelítésnek vannak előnyei és hátrányai, ezért gyakori, hogy egy erősen típusos nyelvet kiegészítenek egy interpreter alapú elemmel. A BeanShell (és a másik elterjedt nyelv a Groovy) pontosan ilyen. Az érdekességet többek között az a lehetőség jelenti, hogy a BeanShell scripteket Java környeztből is tudjuk futtatni, amivel hatékony scriptnyelv kiegészítéshez jutunk. Mi a BeanShell? A BeanShell egy olyan script nyelv, amit a Java virtuális gép tud futtatni, ugyanis annak értelmezőjét
a Javaban megvalósított bsh.Interpreter osztály valósítja meg. A webhelye: http:// www.beanshellorg/ Nem célunk részletesen elemezni az értelmezett és fordított nyelvek előnyeit és hátrányait, de 2 jellemvonást mégis szeretnénk kiemelni, amivel biztosan érdekes lehetőséggel egészíthetjük ki Java programjainkat: 1. A BeanShell scriptek a Java program futása közben is használhatóak, így az őket reprezentáló programszövegek végrehajthatóak. Ugyanakkor ezek a szövegek dinamikusan is előállíthatók előtte, ami érdekes algoritmus szótárak kialakítását teszi 2.1 ábra: BeanShell console lehetővé. Ez a gyakorlatban azt is jelentheti, hogy az adatkonfiguráció mellett alEnnek van egy szöveges alapú testvére is, goritmikus konfigurációkat is rendelhetünk amit ezzel a paranccsal tudunk elindítani (az a programunkhoz. exit(); utasítással tudunk kilépni belőle): 2. A script nyelvek lazábban kezelik az egyéb- j a v a −cp bsh −20 b4 j
a r bsh I n t e r p r e t e r B e a n S h e l l 2 . 0 b4 − by Pat Niemeyer ( pat@pat n e t ) ként nagyon fontos típus fogalmat, de pont bsh % ez a tulajdonságuk lehetővé teszi a hasznáAz Interpreter class arra is használható, hogy latukat ott, ahol csak 2-3 soros programtöegy textfájlban megírt bsh programot futtasredékre van szükség. sunk. Legyen például ez a program, amit a Írjuk be a parancsértelmezőbe az alábbi utasí- p1.bsh fájlba mentettünk: tást, miután megjelenik a 2.1 ábrán mutatott grafikus parancsértelmező. j a v a −cp /home/ j a v a / bean−s h e l l / bsh −2.0 b4 j a r Console 32 bsh . å // 2−1. P r o g r a m l i s t a : p1 b s h f o r ( i =1; i <=10; i ++) { print ( i ) ; } Java programozói könyvtár BeanShell - Könnyűsúlyú scriptek Java nyelvbe ágyazva Futtatás : j a v a −cp bsh −2.0 b4 j a r bsh I n t e r p r e t e r p1 bsh írhatunk metódusokat is, de a Java nyelv rendelkezik néhány olyan
szerkezeti konstrukcióval, amit ez az eszköz (még) nem ismer. A program 1-10-ig kiírja az egész számokat a képernyőre. Gyengített Java típusok használata A Java nyelvet a BeanShell ismeri, így azokat a kiegészítéseket érdemes áttekinteni, amik ehhez A BeanShell létrehozásának volt néhány célja, a képest jelentenek új elemeket. Az igazi újdonnyelvi elemek kialakítását ezek nagymértékben ság a Java eszközeinek gyenge típusossággal való használata, amire egy példa a következő: befolyásolták: Alapvető nyelvi elemek • A Java kiegészítése szkriptelési lehetőségekkel. • Platformfüggetlen shell környezet elérhetősége. • Gyors unit tesztek elkészíthetősége. bsh % map = new HashMap ( ) ; bsh % map . put ( " alma " , 1 2 ) ; bsh % map . put ( " k ö r t e " , 2 1 ) ; bsh % p r i n t (map) ; { alma =12 , k ö r t e =21} bsh % Egy másik egyszerű példa: p r i n t ( System . c u r r e n t T i m e M i l l i s
( ) ) ; 1367078180006 Végül az egyszerűsített, típus nélküli for cik• Nagyobb rendszerekbe való beágyazható- lus: ság. f o r ( i =1; i <=10; i ++) { A Java szintaxis ismerete }; print ( i ) ; A típus elhagyásának lehetősége mindenütt Egy teljes Java programot nem feltétlenül tud futtatni a BeanShell, de ennek nem az az oka, lehetséges, például a kivételkezelés catch ágánál hogy Java utasítások sorozatát ne tudná fut- csak a változó nevét írhatjuk: tatni. Például az alábbi Java program gond nél- try { kül fut: . System . out p r i n t l n ( " I n d u l o k " ) ; System . out p r i n t l n (new Date ( ) ) ; int f a c t = 1 ; f o r ( i n t i = 1 ; i < 8 ; ++i ) { f a c t ∗= i ; System . out p r i n t l n ( i + " f a c t=" + f a c t ) ; } A 1. 2. 3. 4. 5. 6. 7. f u t á s i eredmény v é g e : f a c t =1 f a c t =2 f a c t =6 f a c t =24 f a c t =120 f a c t =720 f a c t =5040 Itt az a szabály, hogy
mindent használhatunk, amit egy metódus törzsében írnánk, sőt } catch ( e ) { p r i n t ( " c au gh t ␣ e x c e p t i o n : ␣ "+e ) ; } A változók hatóköre Van 2 fontos szabály, amit mindig érdemes észben tartani, amikor BeanShell scriptet készítünk: • A deklarált változók láthatósága olyan, mint Java-ban. • A nem deklarált (más szóval: statikus típus nélküli) változók láthatóságát nem módosítják a {} blokkok. 33 Java programozói könyvtár BeanShell - Könnyűsúlyú scriptek Java nyelvbe ágyazva Az alapvető viselkedés a Java nyelvre hasonlít, azonban a program szövege explicit módon nem olyan, hogy minden változó egy osztálynak lenne a tagja, így az ilyen változók hatóköréről érdemes néhány megállapítást tenni. A következő példa mutatja, hogy egy kapcsos zárójelek közötti változó a blokkra lokális, ha típusos. Ez a Java-val való kompatibilitás miatt szükséges. Amennyiben nem típusos
(esetünkben az y), úgy a változó az őt létrehozó blokkon kívül is látható. // k ó d b l o k k { y = 2; // Untyped v a r i a b l e a s s i g n e d i n t x = 1 ; // Typed v a r i a b l e a s s i g n e d } p r i n t ( y ) ; // 2 p r i n t ( x ) ; // Error ! x i s u n d e f i n e d . A fenti jelenség az összes ilyen szituációra igaz, például a for indexváltozó elérhető a ciklusmag után is, amennyiben annak nem volt explicit típusmegadása. A változók módosító szavai Ismerek a Javaban a private, protected, public, final, transient, volatile, static kulcsszavak, ezeket a BeanShell elfogadja, de hatása csak a final nak van. Ugyanakkor nem deklarált változóknál nem használható semmilyen módosítószó. A getter metódusok használata hasonló. Ciklus A Java 1.5-ben bevezetett ciklus használható, sőt annak értelmezése tovább lett tágítva az alábbi típusokra: • Enumeration • Vector • String • StringBuffer (StringBuilder -re nem működik)
• Collection • Iterator • tömb A következő példa egy String karakterein iterál végig és közéjük illeszt egy ’@’ jelet: s t r=" " ; f o r ( char c : "Alma" ) . { s t r += c + "@" ; } print ( str ) ; F u t á s i eredmény : A@l@m@a@ JavaBean szelektorok Java-ban megtanultuk a setter és getter metódusok használatát, azonban a BeanShell mégis a hagyományos hivatkozást részesíti előnyben, azaz a ’.’ karaktert Itt probléma lehet, ha egy prop nevű adattag létezik, mert akkor nem annak a setter/getter metódusa fog meghívódni, hanem magára az adattagra fogunk hivatkozni. Erre a BeanShell ezt a szintaxist vezette be: // A s e t t e r : obj . setProp ( valami ) ; // H e l y e t t e : o b j { " prop " } = v a l a m i ; // Pedig e z t a k a r t u k : o b j . prop = v a l a m i ; 34 Kiterjesztett elágazás A BeanShell switch kulcsszava lehetővé teszik a script nyelveken szokásos „laza” elágazások
megvalósítását, ami a gyakorlatban ezt jelenti, hogy a szelektor bármilyen objektum lehet. A feltétel teljesülését az equals() metódussal vizsgálja az Interpreter : maiNap = new Date ( ) ; switch ( maiNap ) { case m i k u l a s : break ; case k a r a c s o n y : break ; default : } Java programozói könyvtár BeanShell - Könnyűsúlyú scriptek Java nyelvbe ágyazva A fenti összeadást megvalósító script részlet a Java szintaxist követ és típusok is vannak benne. A Java nyelv módszerét ismeri, de itt a catch ágMindez persze típusok nélkül is megy: ban megadhatunk típus nélküli objektumot is, addTwoNumbers ( a , b ) ahogy a következő példa mutatja: { Kivételkezelés try { . } catch ( e ) { print ( e ) ; } Importálás } return a + b ; Ekkor azonban futás közben oldódik fel, hogy a és b típusától függően a ’+’ operátornak mi lesz a jelentése: // A f o o 3 l e s z . f o o = add ( 1 , 2 ) ; Osztályok vagy csomagok
használatára a Java // A f o o New York l e s z nyelv import utasítása használható. Érdekes le- f o o = add ( "New" , " ␣ York " ) ; hetőség az un. szuper import, aminek az a léLátható, hogy típus nélküli esetben nem kell nyege, hogy a classpath-ról elérhető összes class megadni a visszatérési típust, így bármilyent automatikusan importálódik: vissza is lehet adni. A private, protected, pubimport ∗ ; lic, synchronized, final, native, abstract és static Néha felvetődhet a kérdés, hogy egy osztály módosítók elfogadottak, de jelenleg hatása csak melyik jar fájlban volt, ezért ki is lehet íratni a a synchronized kulcsszónak van. A throws ellenőrzött, de nem kierőszakolt scriptben a which() függvény segítségével: bsh % which ( j a v a . l a n g S t r i n g ) ; Jar : f i l e : / usr / java / j2sdk1 . 4 0 / j r e / l i b / r t j a r Látni fogjuk, hogy a BeanShell rendelkezik azokkal a fontosabb, előre megírt
parancsokkal, amik mindegyik shell részét szokták képezni. Ezek importálására egy külön függvényhívás szolgálhat: importCommands ( " / bsh /commands" ) ; Scriptben készített elemek Metódusok Egy metódus hasonlóan készíthető, mint Javaban. Nézzük az alábbi példát: i n t addTwoNumbers ( i n t a , i n t b ) { return a + b ; } Scope módosító Néha ugyanolyan nevű egy külső és a lokális változó. A kérdés az, hogy egy metódusból milyen módon tudunk hivatkozni a külső változóra Erre szolgál a super kulcsszó, aminek a megértéséhez szintén segítsen egy példa: int a = 4 2 ; foo () { int a = 9 7 ; print ( a ) ; p r i n t ( super . a ) ; } foo () ; // p r i n t s 97 , 42 Objektumok A Java osztályok létrehozását ismeri a BeanSEnnek meghívása semmi különlegességet hell, de működik a JavaScripthez hasonló menem mutat: chanizmus is, amit a következő példa mutat: sum = addTwoNumbers ( 5 , 7 ) ; 35 Java programozói
könyvtár BeanShell - Könnyűsúlyú scriptek Java nyelvbe ágyazva foo () { i n t bar = 4 2 ; return t h i s ; } fooObject = foo () ; p r i n t ( f o o O b j e c t . bar ) ; // p r i n t s 4 2 ! A foo() metódus visszaad egy objektumot a this érték miatt, azaz ekkor ez a függvény úgy viselkedik, mint egy konstruktor. Ezután már a fooObject objektumon keresztül elérhetjük a bar adattagot. A foo() függvényen belül metódusokat is létrehozhatunk: foo () { . bar ( ) { . } . } Interfészek Az actionPerformed() metódus látható a this hatókörében, amiatt használható is eseménykezelőként. Természetesen a legjobb megoldás, amikor mindezt egy objektumba zárjuk, ahogy ebben a példában is tesszük: messageButton ( message ) { JButton b ut to n = new JButton ( " P r e s s ␣Me" ) ; bu tt on . a d d A c t i o n L i s t e n e r ( t h i s ) ; JFrame frame = frame ( bu tt on ) ; } actionPerformed ( e ) { p r i n t ( message ) ; frame . s e t V i s i b
l e ( f a l s e ) ; } messageButton ( "Hey␣ you ! " ) ; messageButton ( " Another ␣ message . " ) ; A messageButton() konstruktor képes egy (névtelen grafikus nyomógomb) objektumot létrehozni és implementálja is annak actionPerformed() metódusát. A fenti BeanShell program egymás után 2 nyomógombot tesz ki és annak megnyomására kiírja a megfelelő szövegeket. Nézzük például a Java ActionListener interface Párhuzamos szálak egy lehetséges implementációját! b u t t o n H a n d l e r = new A c t i o n L i s t e n e r ( ) { actionPerformed ( event ) { p r i n t ( event ) ; } }; button = new JButton ( ) ; button . a d d A c t i o n L i s t e n e r ( b u t t o n H a n d l e r ) ; frame ( button ) ; A buttonHandler egy ActionListener típusú objektum, ami a button nyomógombnak átadható eseménykezelőként. Egy script láthatósági körébe tartozó, megfelelő nevű és paraméterezésű metódus szintén használható:
actionPerformed ( event ) { p r i n t ( event ) ; } button = new JButton ( "Foo ! " ) ; button . a d d A c t i o n L i s t e n e r ( t h i s ) ; frame ( button ) ; 36 Amennyiben egy scriptelt class belsejében a run() metódust is megvalósítjuk, az képes futni egy Java futási szálon, ahogy a következő kis példa is szemlélteti: Futok ( ) . { run ( ) . { p r i n t ( " Futok . " ) ; } return t h i s ; } Rohanok ( ) { run ( ) { p r i n t ( "Rohanok . " ) ; } return t h i s ; } f u t = Futok ( ) ; rohan = Rohanok ( ) ; // S t a r t two t h r e a d s on f o o . run ( ) Java programozói könyvtár BeanShell - Könnyűsúlyú scriptek Java nyelvbe ágyazva new Thread ( f u t ) . s t a r t ( ) ; new Thread ( rohan ) . s t a r t ( ) ; 17. sorban tekinthetjük meg az ip Interpreter példány létrehozását, majd a 18-21 sorok közötti set() metódusokkal átadjuk a Java program változóit a BeanShell most létrehozott ip interpreterének. A
set() 1 paramétere annak a változónak a neve, ahogy azt a BeanShell is látja, míg a 2. paraméter az átadott érték, ami nem csak egy skalár, hanem egy objektum referencia is lehet természetesen. Az 1 és 2 paraméterek által létrehozott megfeleltetést a két környezet (Java és BeanShell) közötti változó mappingnek nevezzük. A 23 sor eval() metódusa egy BeanShell script részletet futtat le, azaz az os objektum b adattagjának az értékét 3333 -ra állítja. A végén a Java program kiírja az os.b értékét és láthatjuk, hogy a lefuttatott script darabka megtette a feladatát, a képernyőn 3333 jelent meg. F u t á s i eredmény : Futok . Rohanok . Beágyazás a Java nyelvbe Egyszerű használat Az egyik leghatékonyabb és ötletesebb használata a BeanShell-nek az, amikor egy Java programból, beágyazva használjuk. Erre nézzünk meg mindjárt egy példát (2-1. Programlista)! Láthatjuk, hogy a lényeg most is az Interpreter class 1 példányának
létrehozásában és használatában van. A példában az Os osztály (28-33 sorok) csak egy egyszerű value object, aminek egyetlen feladata a 3 tagváltozó befoglalása. A 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 // 2−1. P r o g r a m l i s t a : H e l l o j a v a package o r g . c s b e a n s h e l l ; import j a v a . u t i l Date ; import bsh . E v a l E r r o r ; import bsh . I n t e r p r e t e r ; public c l a s s H e l l o { public s t a t i c void main ( S t r i n g [ ] a r g s ) throws E v a l E r r o r { Os o s = new Os ( ) ; os . a = 2 ; os . c = 10; I n t e r p r e t e r i p = new I n t e r p r e t e r ( ) ; // C o n s t r u c t an i n t e r p r e t e r i p . s e t ( " f o o " , 5 ) ; // S e t v a r i a b l e s i p . s e t ( " d a t e " , new Date ( ) ) ; } } ip . s e t ( " os " , os ) ; System . out p r i n t l n ( o s b ) ; i p . e v a l ( " o s b=3333" ) ; // Fut a s
c r i p t System . out p r i n t l n ( o s b ) ; public c l a s s Os { public i n t a ; public i n t b ; public i n t c ; } 37 Java programozói könyvtár BeanShell - Könnyűsúlyú scriptek Java nyelvbe ágyazva Természetesen a Hello class-ban bemutattakon kívül sokkal komplexebb lehetőségeink vannak, amire nemsokára példát is adunk. A 19 sorban átadott dátum értékét Java oldalról így kérhetjük vissza: public c l a s s MyValueObject { public Os o s ; public s t a t i c c l a s s B e l s o C l a s s { public Map<S t r i n g , S t r i n g > mapå ; } Date d a t e = ( Date ) i p . g e t ( " d a t e " ) ; Ez már egy példa, amikor az inline script segítségével egy beszúrt algoritmusként szorzást is végzünk: i p . e v a l ( " bar ␣=␣ f o o ∗10 " ) ; System . out p r i n t l n ( i p g e t ( " bar " ) ) ; Végül ez a 2 Java oldalról hívott BeanShell darabka kiírja a dátumot, majd a Java-ból is ismert
javap() metódus segítségével a képernyőre írja a date objektum osztályáról való tudnivalókat, azaz az őt felépítő metódusokat. ip . eval ( " p r i n t ( date ) ; ␣ javap ( date ) " ) ; Sat Apr 27 1 9 : 0 3 : 3 9 GMT 2013 C l a s s c l a s s j a v a . u t i l Date extends c l a s s j a v a å lang . Object public boolean j a v a . u t i l Date e q u a l s ( j a v a l a n g å . Object ) public j a v a . l a n g S t r i n g j a v a u t i l Date å toString () public i n t j a v a . u t i l Date hashCode ( ) public j a v a . l a n g O b j e c t j a v a u t i l Date c l o n e ( ) public i n t j a v a . u t i l Date compareTo ( j a v a u t i l å Date ) public i n t j a v a . u t i l Date compareTo ( j a v a l a n g å Object ) public boolean j a v a . u t i l Date a f t e r ( j a v a u t i l å Date ) public boolean j a v a . u t i l Date b e f o r e ( j a v a u t i l å . Date ) public s t a t i c long j a v a . u t i l Date p a
r s e ( j a v a å lang . String ) . Külső script használata Az nehézkes és nem is eredményezne szép kódot, ha a BeanShell scripteket mindig Java Stringként állítanánk össze, ezért ennek elkerülésére van egy egyszerű megoldás, amit a következő példával ismertetünk: // 2−2. P r o g r a m l i s t a : MyValueObject j a v a package o r g . c s b e a n s h e l l ; import j a v a . u t i l Map ; 38 } int i ; public i n t g e t I ( ) { return i ; } public void s e t I ( i n t i ) { this . i = i ; } public double getD ( ) { return d ; } public void setD ( double d ) { this . d = d ; } public S t r i n g g e t S ( ) { return s ; } public void s e t S ( S t r i n g s ) { this . s = s ; } public B e l s o C l a s s getBc ( ) { return bc ; } public void s e t B c ( B e l s o C l a s s bc ) { t h i s . bc = bc ; } double d ; String s ; B e l s o C l a s s bc=n u l l ; A MyValueObject (2-2. Programlista) egy értékeket tárolni képes class, aminek még egy belső
statikus osztállyal (BelsoClass) megvalósított adattagja is van. Mindezt csak azért készítettük, hogy azt is megmutassuk, hogy bármilyen komplex objektumot átadhatunk egy BeanShell scriptnek, miközben azt most egy külső kulsokod.bsh nevű fájlba (2-3 Programlista) tettük Maga a kulso-kod.bsh csak egyetlen test() metódust tartalmaz, ami átvesz egy MyValueObject objektumot és tesz rajta változtatásokat. Java programozói könyvtár BeanShell - Könnyűsúlyú scriptek Java nyelvbe ágyazva // 2−3. P r o g r a m l i s t a : k u l s o −kod b s h import o r g . c s b e a n s h e l l ∗ ; t e s t ( obj ) { obj . i = 3; obj . d = 2 1 ∗ 3 2 ; o b j . s = " Almafa " ; o b j . bc map = new j a v a u t i l HashMap ( ) ; o b j . bc map put ( " e l s o " , " 32 " ) ; } A 2-4. Programlista a külső script használatát szemlélteti A 6 sorban a BeanShell Interpreter, a 7-9-ben pedig a MyValueObject egy példányát legyártjuk
A 11 sorban az egész vo value object-et átadjuk az ip interpreternek, amit láthatóan ő is vo néven fog ismerni (az első para1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 méterben adtuk ezt a nevet). A 13 sorban van egy lényeges lépés! Az ip változó source() metódushívása betölti és lefuttatja az ip interpreterrel a külső kódot. Itt most semmi végrehajtandó nincs, mindössze a test() metódus ismerete lesz deklarálva az ip számára úgy, mintha azt konzolról beírtuk volna. A 14 sorban lefuttatjuk a test() metódust a BeanShell által már megismertetett saját vo objektumára, ami másfelől Java oldalról is látszik az előzetes összerendelés miatt. A 16-19 sorok kiírásai visszaigazolják, hogy a külső forrásból beolvasott script test() metódusa elvégezte a dolgát, a vo objektum megváltozott. // 2−4. P r o g r a m l i s t a : T e s t B e a n S h e l l S o u r c e j a v a . public s t a t i c void testCaseComplex ( ) throws E x c e p
t i o n { I n t e r p r e t e r i p = new I n t e r p r e t e r ( ) ; MyValueObject vo = new MyValueObject ( ) ; vo . bc = new MyValueObject B e l s o C l a s s ( ) ; i p . s e t ( " vo " , vo ) ; i p . s o u r c e ( " /home/ t a n u l a s / b e a n s h e l l / k u l s o −kod bsh " ) ; i p . e v a l ( " t e s t ( vo ) ; " ) ; } . System . out System . out System . out System . out p r i n t l n ( vo . i ) ; p r i n t l n ( vo . d ) ; p r i n t l n ( vo . s ) ; p r i n t l n ( vo . bc map g e t ( " e l s o " ) ) ; Speciális változók és értékek A BeanShell hatékony használata megkövetelheti, hogy pontosan ismerjük azokat az előre definiált változókat és értékeket, amiket praktikus okok miatt tettek a nyelvbe. Nézzük meg őket röviden! • $ : Visszaadja az legutoljára kiértékelt kifejezés értékét. • bsh: A script nyelv root objektuma, eddig is használtuk már. Ezen keresztül érhető el a legtöbb bsh
szolgáltatás. • bsh.args: A script által kapott paraméterek String tömbként reprezentálva • bsh.cwd : Az aktuális munkakönyvtárat adja vissza. Fontos körülmény annak az ismerete, hogy egy • $ e: A legutolsó el nem kapott kivétel ob- változó definiálva van, azaz van-e már kezdőérjektum. téke. Ezt így lehet megvizsgálni: 39 Java programozói könyvtár BeanShell - Könnyűsúlyú scriptek Java nyelvbe ágyazva // u n d e f i n e d i f ( f o o b a r == void ) Egy tetszőleges változót bármikor definiálatlanra lehet állítani az unset() hívással: a == void ; // t r u e a =5; u n s e t ( " a " ) ; // n o t e t h e q u o t e s a == void ; // t r u e • frame() Megjelenít egy AWT vagy Swing komponenst (például nyomógomb) egy keretben. Forrásprogramok kiértékelése • eval() Egy String-et kiértékel és végrehajt, mint egy BeanShell scriptet. BeanShell parancsok A BeanShell egy valódi shell program, ezért tartalmazza
azokat a parancsokat, amiket egy ilyentől elvárunk. Az alábbiakban kategorizálva röviden áttekintjük őket Az Interpreter mód parancsai • exit() Kilépés az interpreterből. • show() Be lehet vele kapcsolni azt az üzemmódot, amikor az interpreter minden kiértékelés eredményét kiírja. Például egy a=3; értékadás eredményét visszhangozza ilyen formában: <3>. Kapcsolóként működik, azaz a következő show() hívás kikapcsolja ezt az üzemmódot • source() A fájlrendszerből betölti a specifikált fájlt és végrehajtja, mint egy BeanShell scriptet. • sourceRelative() Hasonló a source()hoz, de a fájl helyét a cwd -hez (current working directory) relatív módon kell megadni. • run(), bg() Itt a külső fájl egy új szálon létrehozott másik Interpreter -ben lesz végrehajtva. • exec() Egy külső, operációs rendszerbeli natív programot futtat le. • setAccessibility() Szabályozni lehet vele, a private és protected tagok
elérhetőségét. • server(port) Ekkor a BeanShell egy távoli klienssel is elérhető. Ez egy böngésző vagy a telnet parancs is lehet. Például a server(3456); hívás hatására a böngészőből, a 3456 porton keresztül használható lesz ez a távoli BeanShell. Kiíró utasítások 40 Közhasznú parancsok (utilities) • javap() Egy objektum osztályának a metódusait és adattagjait írja ki. • which() Egy Java class esetén visszaadja azt a jar fájlt, amiből őt betöltötték. • print() Kiírja a standard output-ra a paraméterül kapott értéket. • load(), save() Egy tetszőleges, szerializációra engedélyezett objektumot képes betölteni és elmenteni a fájlrendszerbe. • error() Kiírja a standard error-ra a paraméterül kapott értéket. • object() Egy üres object létrehozása. Java programozói könyvtár BeanShell - Könnyűsúlyú scriptek Java nyelvbe ágyazva • rm() Kitöröl egy fájlt (remove) Változók • clear()
Kitörli az összes változót, metódust és importált neveket az aktuális scope-ból. • mv() Átmozgat egy fájlt másik helyre (move) • unset() A paraméterként megadott String egy változó neve, amit kitöröl a script aktuális látóköréből. • pathToFile() Egy relative path abszolútra alakítása. • cat() Egy textfájl tartalmát kiírja. • setNameSpace() A script aktuális Saját BeanShell parancs scope-jára egy namespace nevet lehet vele beállítani. A BeanShell parancsok scriptelt metódusok vagy lefordított Java class-ok. Vegyük például ezt a nagyon egyszerű scriptet, amit a /home/pat/myCLASSPATH kezelés commands könyvtárba mentettünk: Ezek a parancsok lekérdezik vagy megváltoztat// F i l e : h e l l o W o r l d . b s h ják a classpath-t. helloWorld ( ) { • addClassPath() A meglévő CLASSPATH} hoz hozzátesz egy új elemet. • setClassPath() Beállítja a CLASSPATHt, az előző megszűnik. • getClassPath() Lekérdezi az
aktuális CLASSPATH-t. p r i n t ( " H e l l o ␣World ! " ) ; Ezt parancsként is használhatjuk, : // ha e z t a 2 s o r t k i a d j u k e l ő t t e addClassPath ( " /home/ pat " ) ; importCommands ( " /mycommands" ) ; // h a s z n á l h a t j u k e z t a p a r a n c s o t : helloWorld ( ) ; A két bevezető utasításra azért van szük• reloadClasses() Újra betölt a JVM-be ség, mert az első hozzáteszi a CLASSPATH-ra a egy osztályt vagy az osztályok egy csoport/home/pat könyvtárat, míg a második az alatta ját. lévő mycommands könyvtárból, mint csomagból (package) importál. Amennyiben egy Java class• getClass() Betölt egy osztályt ban vannak az új parancsok, úgy csak a szokásos • getResource() Lekér egy erőforrást a importCommands("com.xyzutils"); forma haszCLASSPATH-ról nálata szükséges. Fájlok és könyvtárak • cd() Könyvtárváltás (change directory). Class loading Alapvető
lehetőségek • pwd() Visszaadja az aktuális könyvtá- A BeanShell kifinomult módon képes az osztályrat (print working directory). betöltéseket és a CLASSPATH-t kezelni, ezért • dir() Kilistázza az aktuális könyvtár érdemes azt röviden áttekinteni. Az osztálybetöltő (Class Loader) a CLASSPATH-on megtartalmát adott helyekről igyekszik az igényelt class-t betölteni. Amennyiben egy megadott jar vagy • cp() Fájlmásolás (copy). 41 Java programozói könyvtár BeanShell - Könnyűsúlyú scriptek Java nyelvbe ágyazva könyvtár nincs rajta, úgy a már említett addC- Remote mód lassPath() metódust hívhatjuk segítségül, amire Ekkor egy másik, távoli shell servletként fut és itt van néhány példa: képes végrehajtani a mi BeanShell scriptünket: addClassPath ( " /home/ pat / j a v a / c l a s s e s " ) ; addClassPath ( " /home/ pat / j a v a / m y s t u f f . j a r " ) ; addClassPath ( new URL( " h t t p : / /
mys erver /~ pat /å somebeans . j a r " ) ) ; j a v a bsh . Remote h t t p : // l o c a l h o s t / b s h s e r v l e t /å e v a l t e s t 1 . bsh Lehetőség van egy saját, natív bsh hálózati A következő parancs minden osztályt újraprotokoll használatára is: tölt, ami elérhető: reloadClasses () ; j a v a bsh . Remote bsh : // l o c a l h o s t : 1 2 3 4 / t e s t 1 å bsh Ennek létezik egy finomabban hangolt váltoArról már volt szó, hogy egy futó BeanShell zata is, ahol megadjuk azt a csomagot, amit újra esetén a server(port); paranccsal kapcsolható be szeretnénk tölteni: a listener, azaz ezután ezen a porton tudjuk vele r e l o a d C l a s s e s ( " mypackage . ∗ " ) ; a fenti kliens kéréseket megtenni. A böngészőbe Természetesen egy konkrét osztály ismételt is beírhatjuk a fenti URL-t, aminek a hatására egy command ablakban távolról vezérelhetjük a betöltésére is van lehetőség: shell-t. Bár kissé nehézkes, de
mindezt termér e l o a d C l a s s e s ( " mypackage EgyClass " ) ; szetesen telnet-en keresztül is elvégezhetjük. Eddig mindig az osztályok újratöltéséről volt szó, de annak manuális első betöltése is lehetséServlet mód ges: name=" f o o . bar MyClass " ; c = g e t C l a s s ( name ) ; // vagy : c = BshClassManager . classForName ( name ) ; A BeanShell használati módjai A BeanShell 5 üzemmódban képes működni: • Konzol módban scriptek futtatása. • Java nyelvbe beágyazott módon. • Remote Server mód • Servlet mód • Applet mód. Az első két üzemmódról már volt szó, az Applet módról pedig nem érdemes írni, mert az mára egy elavúlt technológia. 42 A BeanShell jar fájlban a servlet megvalósítás is rendelkezésre áll, azt azonban a szokásos módon egy war csomaggal kell körbevenni, ahol a web.xml így néz ki: <web−app> < s e r v l e t> <s e r v l e t −name>b s h s e r v l e t</ s e r
v l e t −name> <s e r v l e t −c l a s s>bsh . s e r v l e t B s h S e r v l e t</å s e r v l e t −c l a s s> </ s e r v l e t> <s e r v l e t −mapping> <s e r v l e t −name>b s h s e r v l e t</ s e r v l e t −name> <u r l −p a t t e r n>/ e v a l</ u r l −p a t t e r n> </ s e r v l e t −mapping> </web−app> A használható URL a böngészőben: h t t p : // l o c a l h o s t / b s h s e r v l e t / e v a l Amennyiben helyi scriptet szeretnénk futtatni a távoli servlet segítségével, úgy ezt így tehetjük meg: j a v a bsh . Remote h t t p : // l o c a l h o s t / b s h s e r v l e t /å e v a l t e s t 1 . bsh Java programozói könyvtár BeanShell - Könnyűsúlyú scriptek Java nyelvbe ágyazva A reflective használati mód A metódusok megkeresése A Java önelemzésen alapuló használat azért fon- Az eddigiekben azt mutattuk meg, hogy egy istos, mert néha dinamikus elérésre
vagy valami- mert paraméterezésű metódust milyen módon lyen ötletes megoldásra van szükség. hívhatunk meg. Tekintsük ezt a metódust, ami bsh.BshMethod objektumok tömbjével tér vissza: Az eval() használata t h i s . namespace getMethods ( ) ; Ez a legegyszerűbb formája a reflective stílusEgy konkrét nevű és paraméterezésű metónak, a korábbiakban már sokszor használtuk is, dust is lekérhetünk: íme egy emlékeztető példa: e v a l ( " a =5; " ) ; p r i n t ( a ) ; // 5 Ha ismerjük a metódus nevét és szignatúráját, akkor mi is összerakhatunk egy stringet, amivel így meghívhatjuk a metódust. Készítsük el a következő foo() és bar() metódusokat: // Nincs pa r am é t er e foo () { . } // A neve name=" bar " ; // A p a r a m é t e r e i s i g n a t u r e = new C l a s s [ ] { I n t e g e r .TYPE, String . class }; // És l e k é r j ü k az e r r e i l l e s z k e d ő t : bshMethod = t h i s . namespace getMethod
( name , å signature ) ; // 2 pa r a mé t e r e van bar ( i n t arg1 , S t r i n g a r g 2 ) { . } A BshMethod használata Ekkor az eval() segítségével így hívhatjuk Ilyen értékeket kaptunk a getMethods() metómeg őket: dustól, ugyanakkor ez teszi lehetővé a klassziname=" f o o " ; // i n v o k e f o o ( ) u s i n g e v a l ( ) kus önelemzést. Az alábbi példa mutatja, ahogy e v a l ( name+" ( ) " ) ; lekérdezhetjük egy metódus nevét, paramétereiname=" bar " ; nek és visszatérési értékének típusát: a r g 1 =5; a r g 2=" s t r i n g y " ; e v a l ( name+" ( arg1 , a r g 2 ) " ) ; A this.methods egy String tömb, ami visszaadja a névtérben elérhető összes metódus nevét Az invokeMethod() használata // Önelemzés : name = bshMethod . getName ( ) ; C l a s s [ ] t y p e s = bshMethod . getArgumentTypes ( ) ; C l a s s returnT ype = bshMethod . getReturnType ( ) ; A metódus meghívása pedig így
lehetséges: // Hívás : bshMethod . i n v o k e ( new O b j e c t [ ] { new I n t e g e r å (1) , " blah ! " } , this . i n t e r p r e t e r , this c a l l s t a c k ) ; Ezzel a metódussal 1 lépésben is meghívhatjuk dinamikusan a metódust. Például a bar() így A bshMethod mindent tud magáról (például használható: a metódus nevét), így rajta keresztül egy hívás t h i s . invokeMethod ( " bar " , new O b j e c t [ ] { new å lebonyolítható. Azt kell megadnunk, hogy miInteger (5) , " stringy " } ) ; lyen paraméter értékek mellett, melyik interpreAz 1. paraméter a meghívandó metódus ter futtassa és milyen stack-et használjon hozzá neve, a 2. pedig egy objektum tömb, ami a pa- Ez utóbbi 2 paraméter mindig így jön a futási környezetből, egyszerűen csak át kell adni. ramétereket tartalmazza. 43 Java programozói könyvtár 3. Apache Commons - BeanUtils Apache Commons - BeanUtils A Java nyelv komponens
technológiája a JavaBean fogalomra épül, ami egy olyan osztály, aminek jellemzői vannak (property) és rendelkezik paraméter nélküli konstruktorral is. Gyakran szinonim fogalomként emlegetik a POJO-t, ami a Plain Old Java Object kifejezésből származó mozaikszó. Ebben a részben áttekintjük az Apache BeanUtils könyvtár fontosabb lehetőségeit, hiszen az ott megvalósított funkciókra időnként szükségünk lehet. Az Apache BeanUtils könyvtár egy alacsony- a BeanUtils csomag legfontosabb használati leszintű Java Bean önelemzést és property mani- hetőségeit. pulálást (Bean Introspection Utilities) megvaló- // 3−2. P r o g r a m l i s t a : Gyumolcs j a v a sító programozói csomag. package o r g . c s b e a n u t i l s ; Szabványos JavaBean A Java egyik legismertebb fogalma a JavaBean. Ez egy olyan egyszerű class, aminek birtokolnia kell egy paraméter nélküli konstruktort. Kiemelt feladata, hogy a komponens technológiáknál megismert
property adatokat tároljon, amiket setter és getter metódusokkal lehet beállítani, illetve elérni Az alábbiakban látható Vitamin osztály (3-1 Programlista) egy JavaBean Megjegyezzük, hogy nem adtunk meg explicit konstruktort, ezért a rendszer generál neki egy alapértelmezettet, ami paraméter nélküli. // 3−1. P r o g r a m l i s t a : Vitamin j a v a package o r g . c s b e a n u t i l s ; public c l a s s Vitamin { S t r i n g name ; double volumen ; } public S t r i n g getName ( ) { return name ; } public void setName ( S t r i n g name ) { t h i s . name = name ; } public double getVolumen ( ) { return volumen ; } public void setVolumen ( double volumen ) { t h i s . volumen = volumen ; } A Gyumolcs class (3-2. Programlista) egy kicsit összetettebb JavaBean, tartalmaz adattagként egy Vitamin osztálybeli változót is Ezen osztály segítségével a továbbiakban bemutatjuk 44 import import import import java java java java . . . . util util util util
. ArrayList ; . HashMap ; . List ; . Map ; public c l a s s Gyumolcs { S t r i n g name ; int p r i c e ; String quality ; double w e i g h t ; S t r i n g [ ] props ; L i s t <S t r i n g > p r o p L i s t ; Map<S t r i n g , S t r i n g > propMap ; Vitamin v i t a m i n ; public Gyumolcs ( ) { super ( ) ; p r o p s = new S t r i n g [ 3 ] ; p r o p L i s t = new A r r a y L i s t <S t r i n g >() ; propMap = new HashMap<S t r i n g , S t r i n g >() ; v i t a m i n = new Vitamin ( ) ; } public L i s t <S t r i n g > g e t P r o p L i s t ( ) { return p r o p L i s t ; } public void s e t P r o p L i s t ( L i s t <S t r i n g > p r o p L i s t ) { this . propList = propList ; } public Vitamin g e t V i t a m i n ( ) { return v i t a m i n ; } public void s e t V i t a m i n ( Vitamin v i t a m i n ) { this . vitamin = vitamin ; } public Map<S t r i n g , { return propMap ; } S t r i n g > getPropMap ( ) public void setPropMap (Map<S t
r i n g , { t h i s . propMap = propMap ; } public S t r i n g [ ] { return p r o p s ; } getProps ( ) S t r i n g > propMap ) Java programozói könyvtár public void s e t P r o p s ( S t r i n g [ ] { this . props = props ; } Apache Commons - BeanUtils props ) public S t r i n g getName ( ) { return name ; } public i n t g e t P r i c e ( ) { return p r i c e ; } price ) public S t r i n g g e t Q u a l i t y ( ) { return q u a l i t y ; } public void s e t Q u a l i t y ( S t r i n g { this . quality = quality ; } A JavaBean jellemzők elérése Az Apache BeanUtils könyvtárat a project hivatalos webhelyéről lehet letölteni: http://commons.apacheorg/proper/ commons-beanutils/. Ebben a pontban áttekintjük, hogy egy objektum adattagjait milyen dinamikus eszközökkel lehet elérni. quality ) public double g e t W e i g h t ( ) { return w e i g h t ; } } S t r i n g c l a s s N a m e = " o r g . c s b e a n u t i l s Gyumolcs " ; C l a s s b e a n
C l a s s = C l a s s . forName ( c l a s s N a m e ) ; Object beanInstance = beanClass . newInstance ( ) ; return ( Gyumolcs ) b e a n I n s t a n c e ; } . } // end c l a s s public void setName ( S t r i n g name ) { t h i s . name = name ; } public void s e t P r i c e ( i n t { this . p r i c e = p r i c e ; } public s t a t i c Gyumolcs c r e a t e G y u m o l c s ( ) throws å Exception { public void s e t W e i g h t ( double w e i g h t ) { this . weight = weight ; } Még itt az elején bemutatjuk a bizonyára sokak által ismert dinamikus osztály példány létrehozás szokásos módját (3-3. Programlista) A példát a createGyumolcs() metódus mutatja be nekünk, ami visszaad egy Gyumolcs objektumot. A className változó tartalmazza annak a class-nak a teljes nevét, amiből gyártani szeretnénk 1 példányt. A beanClass objektum egy meta objektum, ami egy betöltött Gyumolcs osztályra mutat. Az objektum legyártását a beanClassnewInstance() sor valósítja
meg, azaz megkérjük a meta objektumot, hogy a maga képéről készítsen el egy class instance-t, amit az utolsó sorban vissza is adunk a metódus hívójának. // 3−3. P r o g r a m l i s t a : Test j a v a package o r g . c s b e a n u t i l s ; A jellemzők elérése A 3-4. Programlista testGetSetProperty() metódusa bemutatja nekünk azt a módszert, ahogy a BeanUtils támogatja a bean property-k elérését és értékük megváltoztatását. Az alma változó egy Gyumolcs példányra mutat. Az alma.setName() hívás még az ismert módon állítja be a name property értékét Starking-ra A jellemzők elérését a PropertyUtils osztály támogatja, rendelkezik néhány statikus, saját felfogású setter és getter metódussal. A példában használt PropertyUtils.getProperty() hívás első paramétere az a bean (esetünkben az alma), amelyiknek a második paraméterben megadott nevű jellemzőjének (most ez name) értékét szeretnénk visszakapni. A metódus
Object-et ad vissza, ezért String-re cast-oltuk, majd a következő sor kiírása visszaigazolta, hogy eredményesek vagyunk, a képernyőn megjelent a Starking szó. A tesztmetódus utolsó 2 sora a jellemző beállítást szemlélteti Ehhez a PropertyUtilssetProperty() használható, ebben a paraméterezésben: import j a v a . l a n g r e f l e c t I n v o c a t i o n T a r g e t E x c e p t i o n ; import j a v a . u t i l L i s t ; import o r g . a p a c h e commons b e a n u t i l s P r o p e r t y U t i l s ; 1. A bean-re hivatkozó változó public c l a s s T e s t { 2. A bean jellemző neve 45 Java programozói könyvtár Apache Commons - BeanUtils 3. A beállítandó érték (esetünkben most Gol- propList lista jellemző 1 tagjának (azaz a fizikai den) 2. tagot) eddigi kerek értékét kerekded -re cseréltük Ez egy indexelt, írásra való property elérés // 3−4. P r o g r a m l i s t a : Test j a v a A for ciklusban hagyományos és a most tanult
package o r g . c s b e a n u t i l s ; indexelt módon való eléréssel is kiírtuk a képimport j a v a . l a n g r e f l e c t I n v o c a t i o n T a r g e t E x c e p t i o n ; ernyőre propList jellemzőt, ami visszaigazolta a import j a v a . u t i l L i s t ; import o r g . a p a c h e commons b e a n u t i l s P r o p e r t y U t i l s ; helyes működést, ugyanis a 2. listaelem kerekded public c l a s s T e s t értékű lett. { . public s t a t i c void t e s t G e t S e t P r o p e r t y ( ) throws å Exception { S t r i n g propValue = null ; Gyumolcs alma = c r e a t e G y u m o l c s ( ) ; alma . setName ( " S t a r k i n g " ) ; S t r i n g name = ( S t r i n g ) P r o p e r t y U t i l s . g e t P r o p e r t y ( å alma , "name" ) ; System . o u t p r i n t l n ( name ) ; P r o p e r t y U t i l s . s e t P r o p e r t y ( alma , "name" , " Golden " ) ; System . o u t p r i n t l n ( alma name ) ; } . } // end c l
a s s // 3−5. P r o g r a m l i s t a : Test j a v a package o r g . c s b e a n u t i l s ; import j a v a . l a n g r e f l e c t I n v o c a t i o n T a r g e t E x c e p t i o n ; import j a v a . u t i l L i s t ; import o r g . a p a c h e commons b e a n u t i l s P r o p e r t y U t i l s ; public c l a s s T e s t { . public s t a t i c void t e s t I n d e x e d L i s t P r o p e r t y ( ) throws å Exception { Gyumolcs alma = c r e a t e G y u m o l c s ( ) ; alma . g e t P r o p L i s t ( ) add ( " p i r o s " ) ; alma . g e t P r o p L i s t ( ) add ( " k e r e k " ) ; Indexelt jellemzők elérése P r o p e r t y U t i l s . s e t I n d e x e d P r o p e r t y ( alma , " p r o p L i s t " , å 1 , " kerekded " ) ; Egy JavaBean property lehet kollekció vagy tömb is. A Gyumolcs osztályban ezek az adattagok indexelt elemeket tartalmaznak: f o r ( i n t i =0; i <alma . g e t P r o p L i s t ( ) s i z e ( ) ; i
++) { System . o u t p r i n t l n ( alma g e t P r o p L i s t ( ) g e t ( i ) ) ; String s = ( String ) PropertyUtils .å g e t I n d e x e d P r o p e r t y ( alma , " p r o p L i s t " , i ) ; System . o u t p r i n t l n ( s ) ; } • String[] props } . } // end c l a s s Mindez persze a leghagyományosabb indexelt elérésű konstrukcióra, a Java tömbre is műFontos, hogy az ilyen típusú elemek dinamikus ködik, ahogy azt a 3-6. Programlista be is mukezelése is megoldott legyen, amit a PropertyU- tatja Ez a tömb esetünkben most a props jeltils class alábbi metódusai meg is tesznek: lemző. A működés megegyezik a listás esettel • List<String> propList • setIndexedProperty(bean, property, index, value) • getIndexedProperty(bean, property, index) A következőkben 2 példát is bemutatunk, mert szeretnék demonstrálni a tömbök és a listák használatát is. Az első példánk (3-5 Programlista) a listák indexelt elérését mutatja,
erre a propList bean property-t használtuk fel, amihez a piros és kerek értékeket hozzá is tettük 2 darab add() metódushívással. A következő sorban jön az első újdonság, ugyanis a PropertyUtils class setIndexedProperty() módszerével a 46 // 3−6. P r o g r a m l i s t a : Test j a v a package o r g . c s b e a n u t i l s ; import j a v a . l a n g r e f l e c t I n v o c a t i o n T a r g e t E x c e p t i o n ; import j a v a . u t i l L i s t ; import o r g . a p a c h e commons b e a n u t i l s P r o p e r t y U t i l s ; public c l a s s T e s t { . public s t a t i c void t e s t I n d e x e d A r r a y P r o p e r t y ( ) throwså Exception { Gyumolcs alma = c r e a t e G y u m o l c s ( ) ; alma . g e t P r o p s ( ) [ 0 ] = " s á r g a " ; alma . g e t P r o p s ( ) [ 1 ] = " k e r e k " ; alma . g e t P r o p s ( ) [ 2 ] = " f i n o m " ; P r o p e r t y U t i l s . s e t I n d e x e d P r o p e r t y ( alma , "
p r o p s " , 1 , å " kerekded " ) ; f o r ( i n t i =0; i <alma . g e t P r o p s ( ) l e n g t h ; i ++) { System . o u t p r i n t l n ( alma g e t P r o p s ( ) [ i ] ) ; String s = ( String ) PropertyUtils .å g e t I n d e x e d P r o p e r t y ( alma , " p r o p s " , i ) ; Java programozói könyvtár System . o u t p r i n t l n ( s ) ; } } . } // end c l a s s Apache Commons - BeanUtils System . o u t p r i n t l n ( alma getPropMap ( ) g e t ( " í z " ) ) ; S t r i n g s = ( S t r i n g ) P r o p e r t y U t i l s . g e t M a p p e d P r o p e r t yå ( alma , " propMap " , " s z i n " ) ; System . o u t p r i n t l n ( s ) ; } . } // end c l a s s Map típusú jellemzők elérése A mai korszerű nyelveknek a Map adatszerkezet fontos konstrukciója. Ezt asszociatív elérésű tömbnek is tekinthetjük, ugyanis nem egy fizikai index választja ki az onnan megkapott értéket, hanem egy objektum
értéke, tipikusan sok esetben egy String értéke. Tekintsük a Gyumolcs osztály propMap jellemzőjét, ami egy Map. A 3-7. Programlista példázza ennek a PropertyUtils class segítségével történő írásra-olvasásra való elérését. Programozói szemszögből láthatjuk, hogy a használata hasonló az indexelt eléréssel, mindössze 2 lényeges változtatásra volt szükség. A setMappedProperty() és getMappedProperty metódusokat kell használni Mindkét metódus első 3 paramétere az adott bean, annak a megfelelő nevű jellemzője és a kulcsérték, amivel elérjük a Map egyik elemét. A setMappedProperty() 4 paramétere az az érték, amire a kiválasztott elemet be szeretnénk állítani. A fenti 2 metódus paraméterezése természetszerűleg annyiban változott, hogy ahol index volt, egy Map kulcsértéknek kell szerepelnie. Ez esetünkben most írásnál az íz, lentebb az olvasásnál pedig a szin String érték // 3−7. P r o g r a m l i s t a : Test j a v
a package o r g . c s b e a n u t i l s ; import j a v a . l a n g r e f l e c t I n v o c a t i o n T a r g e t E x c e p t i o n ; import j a v a . u t i l L i s t ; import o r g . a p a c h e commons b e a n u t i l s P r o p e r t y U t i l s ; public c l a s s T e s t { . public s t a t i c void t e s t M a p P r o p e r t y ( ) throws å Exception { Gyumolcs alma = c r e a t e G y u m o l c s ( ) ; alma . getPropMap ( ) put ( " s z i n " , " z ö l d " ) ; alma . getPropMap ( ) put ( " a l a k " , " k e r e k " ) ; P r o p e r t y U t i l s . s e t M a p p e d P r o p e r t y ( alma , " propMap " , " í z å " , " finom " ) ; System . o u t p r i n t l n ( alma getPropMap ( ) g e t ( " s z i n " ) å ); System . o u t p r i n t l n ( alma getPropMap ( ) g e t ( " a l a k " ) å ); A beágyazott jellemzők elérése Tipikus eset, hogy valamelyik property maga is egy bean,
aminek persze saját jellemzője is van, amit ebben a kontextusban emiatt beágyazott property-nek nevezünk. A 3-8 Programlista ezt az esetet vizsgálja, ahol a vitamin jellemző egy Vitamin bean A példa bemutatja, hogy a vitamin tagváltozó saját name és volumen jellemzőihez a PropertyUtils class erre kitalált getNestedProperty() és setNestedProperty() statikus metódusai milyen módon férnek hozzá. Láthatjuk, hogy a trükk egészen egyszerű, a beágyazott property nevét minősített névként (vitamin.name, vitaminvolumen) kell átadni a metódusoknak, ettől eltekintve a használat megegyezik a nem beágyazott jellemző esetével // 3−8. P r o g r a m l i s t a : Test j a v a package o r g . c s b e a n u t i l s ; import j a v a . l a n g r e f l e c t I n v o c a t i o n T a r g e t E x c e p t i o n ; import j a v a . u t i l L i s t ; import o r g . a p a c h e commons b e a n u t i l s P r o p e r t y U t i l s ; public c l a s s T e s t { . public s t a
t i c void t e s t N e s t e d P r o p e r t y ( ) throws å Exception { Gyumolcs alma = c r e a t e G y u m o l c s ( ) ; alma . g e t V i t a m i n ( ) setName ( "C" ) ; alma . g e t V i t a m i n ( ) setVolumen ( 3 2 5 4 ) ; System . o u t p r i n t l n ( alma g e t V i t a m i n ( ) getName ( ) å ); String s = ( alma , double v = ( alma , System . o u t ( String ) PropertyUtils . getNestedPropertyå " v i t a m i n . name" ) ; ( Double ) P r o p e r t y U t i l s . g e t N e s t e d P r o p e r t y å " v i t a m i n . volumen " ) ; println ( v ) ; P r o p e r t y U t i l s . s e t N e s t e d P r o p e r t y ( alma , " v i t a m i n å volumen " , 4 1 . 0 ) ; v = ( Double ) P r o p e r t y U t i l s . g e t N e s t e d P r o p e r t y ( alma , å " v i t a m i n . volumen " ) ; System . o u t p r i n t l n ( v ) ; } . } // end c l a s s 47 Java programozói könyvtár Apache Commons - BeanUtils típusú zoldseg
változót. A testBasic() tesztmetódus utolsó 2 sorában a name property írását A PropertyUtils class képes kezelni egy már lé- és olvasását mutattuk be. tező JavaBean osztályt, azonban arra is szük- // 3−9. P r o g r a m l i s t a : TestDynaBean j a v a ség lehet, hogy egy osztály jellemzőit futás köz- package o r g . c s b e a n u t i l s ; ben alakítsuk ki. A BeanUtils csomag erre egy import o r g a p a c h e commons b e a n u t i l s BasicDynaBean ; o r g . a p a c h e commons b e a n u t i l s B a s i c D y n a C l a s s ; interface-t definiált, aminek a neve DynaBean. import import o r g . a p a c h e commons b e a n u t i l s B e a n U t i l s ; import o r g . a p a c h e commons b e a n u t i l s DynaBean ; A továbbiakban bemutatjuk, hogy milyen mó- import o r g . a p a c h e commons b e a n u t i l s DynaProperty ; don lehet olyan objektumokat készíteni, amik import o r g . a p a c h e commons b e a n u t i l s WrapDynaBean ;
ezen az interface-en keresztül manipulálhatóak. {public c l a s s TestDynaBean . Ezek nem klasszikus JavaBean-ek, de azokhoz public s t a t i c void t e s t B a s i c ( ) throws E x c e p t i o n { hasonlóan lehet használni. DynaProperty [ ] p r o p s = new DynaProperty [ ] Dinamikus JavaBean { Az alap dinamikus JavaBean A dinamikus bean-ek megismerésére tekintsük a TestDynaBean osztályt (3-9. Programlista) Létrehozunk futás közben egy olyan bean-t, aminek a következő típusú property-ei vannak: • java.utilMap • egy Vitamin példányokból álló tömb • String jellemző Egy DynaProperty tömbben (esetünkben most a neve props) írhatjuk le a létrehozandó bean jellemzőit, ahol a tömb egyes elemei a következő információkat tárolják el: • a jellemző neve (például esetünkben most ezek: jellemzok, vitamin, name, latinName) és • az osztályának teljes minősítéssel megadott megnevezése A jellemzők ismeretében megkonstruálható egy
BasicDynaClass metaobjektum, ami maga az új bean osztály. Paraméterül megadtuk, hogy zoldseg a neve és a props tömbben lévő proprty-k alapján képezze a saját adattagjait. A dynaClass metaobjektum newInstance() metódusa segítségével már létre is tudtuk hozni a DynaBean 48 }; } . } new DynaProperty ( " j e l l e m z o k " , j a v a . u t i l Map å class ) , new DynaProperty ( " v i t a m i n " , o r g . c s b e a n u t i l s å Vitamin [ ] . c l a s s ) , new DynaProperty ( "name" , S t r i n g . c l a s s ) , new DynaProperty ( " l a t i n N a m e " , S t r i n g . c l a s s ) B a s i c D y n a C l a s s d y n a C l a s s = new B a s i c D y n a C l a s s ( "å z o l d s e g " , null , props ) ; DynaBean z o l d s e g = d y n a C l a s s . n e w I n s t a n c e ( ) ; z o l d s e g . s e t ( "name" , " k r u m p l i " ) ; System . o u t p r i n t l n ( z o l d s e g g e t ( "name"
) ) ; A JavaBean becsomagolása A BeanUtils DynaBean arra is képes, hogy becsomagoljon egy szabványos JavaBean-t, aminek a jellemzőit ezután név szerint, a saját set és get metódusával érje el. Mindezt a 3-10 Programlista testWrapper() metódusán tanulmányozhatjuk A Gyumolcs nevű JavaBean-t ismerjük, ennek a gyumolcsBean változón keresztül elérhető példányát fogjuk becsomagolni egy DynaBean objektumba. Az ilyen wrapping-re alkalmas osztály neve a WrapDynaBean A példában a wrapper változó egy már becsomagolt gyumolcsBean referenciáját tartalmazza Ahogy a testWrapper() utolsó 4 sorából látjuk, ez már egy ugyanolyan DynaBean, mintha mi konstruáltuk volna meg, ugyanakkor a jellemzők nevét használhatjuk a get és set elérésekhez. // 3−10. P r o g r a m l i s t a : TestDynaBean j a v a package o r g . c s b e a n u t i l s ; import import import import org org org org . a p a c h e commons . a p a c h e commons . a p a c h e commons . a p
a c h e commons beanutils beanutils beanutils beanutils . BasicDynaBean ; . BasicDynaClass ; . BeanUtils ; . DynaBean ; Java programozói könyvtár import o r g . a p a c h e commons b e a n u t i l s DynaProperty ; import o r g . a p a c h e commons b e a n u t i l s WrapDynaBean ; public c l a s s TestDynaBean { . public s t a t i c void t e s t W r a p p e r ( ) throws E x c e p t i o n { Gyumolcs gyumolcsBean = new Gyumolcs ( ) ; gyumolcsBean . setName ( "Alma" ) ; gyumolcsBean . s e t P r i c e ( 3 2 0 ) ; } . } DynaBean wrapper = new WrapDynaBean ( gyumolcsBean å ); S t r i n g name = ( S t r i n g ) wrapper . g e t ( "name" ) ; i n t p r i c e = ( I n t e g e r ) wrapper . g e t ( " p r i c e " ) ; System . o u t p r i n t l n ( name ) ; System . o u t p r i n t l n ( p r i c e ) ; Az SQL ResultSet becsomagolása A BeanUtils képes a JDBC ResultSet eredményt is DynaBean interface-en keresztül prezentálni. Ennek létezik
online és offline változata is, most először nézzük az elsőt (3-11. Programlista)! Az rs változó az ismert SQL kurzort tartalmazza. A példában azt mutattuk meg, hogy ezt a ResultSetDynaClass segítségével miként csomagolhatjuk be, illetve kérhetünk le ettől a csomagolt objektumtól egy Iterator -t, amit rows-nak neveztünk el. A while ciklus belsejében látható, ahogy az iterátor segítségével végigmegyünk az eredményhalmaz sorain és egy DynaBean interfaceszel rendelkező row objektumhoz férünk minden lépésben. A ciklusból kilépve lezárjuk az adatbázis eléréshez szükséges erőforrásokat // 3−11. P r o g r a m l i s t a : Online R e s u l t S e t . C o n n e c t i o n conn = . ; S t a t e m e n t stmt = conn . c r e a t e S t a t e m e n t ( ) ; R e s u l t S e t r s = stmt . e x e c u t e Q u e r y ( " s e l e c t ␣ a c c o u n t i d , ␣name␣ from ␣ c u s t o m e r s " ) ; I t e r a t o r rows = (new R e s u l t S e t D y n
a C l a s s ( r s ) ) . å iterator () ; while ( rows . hasNext ( ) ) { DynaBean row = ( DynaBean ) rows . n e x t ( ) ; System . o u t p r i n t l n ( " Account ␣ number ␣ i s ␣ " + row . g e t ( " a c c o u n t i d " ) + " ␣ and ␣name␣ i s ␣ " + row . g e t ( "name" ) ) ; } rs . close () ; stmt . c l o s e ( ) ; . Az SQL ResultSet becsomagolása (offline) A 3-12. Programlista a JDBC ResultSet offline használatát tanítja meg, amihez a RowSetDyna- Apache Commons - BeanUtils Class osztály használata szükséges. Ez utóbbi osztály szintén az eredményhalmazt csomagolja be, de ennek tartalmát át is másolja magába, ami lehetővé teszi, hogy még a használata előtt lezárjuk az adatbázishoz kötődő erőforrásokat. A példában az rsdc változót használtuk, aminek a getRows() metódusa ad vissza egy olyan listát, aminek az elemeit már a DynaBean interface-en keresztül is elérhetjük. // 3−12. P r o g r a m
l i s t a : Offline ResultSet . C o n n e c t i o n conn = . ; S t a t e m e n t stmt = conn . c r e a t e S t a t e m e n t ( ) ; R e s u l t S e t r s = stmt . e x e c u t e Q u e r y ( "SELECT␣ " ) ; RowSetDynaClass r s d c = new RowSetDynaClass ( r s ) ; rs . close () ; stmt . c l o s e ( ) ; .; // Return c o n n e c t i o n t o p o o l L i s t rows = r s d c . getRows ( ) ; .; // Process t h e rows as d e s i r e d A lusta Dinamikus JavaBean A lusta jelző most a programozóra utal, aki gyorsabban szeretne hozzáférni egy DynaBean objektumhoz. Ezt a BeanUtils LazyDynaBean class támogatja is, ahogy azt a 3-13. Programlista be is mutatja. Nincs más dolgunk csak létrehozni egy LazyDynaBean példányt, amit esetünkben most egy dynaBean változón keresztül látunk. Az egyszerű, a mappelt és az indexelt elérést is megmutatja a lustaBean() metódus. Látható, hogy egyszerű esetben csak kitalálunk egy property nevet (most ez foo) és már tehetjük is
bele az értéket. A mapped használat sem nehezebb, de ott még a kulcs nevét (esetünkben: title és surname) is meg kellett adnunk. Az indexelt elérés is hasonló, de itt az indexet kell megadni a kulcs neve helyett. // 3−13. P r o g r a m l i s t a : LazyDynaBean package o r g . c s b e a n u t i l s ; import import import import import import org org org org org org . a p a c h e commons . a p a c h e commons . a p a c h e commons . a p a c h e commons . a p a c h e commons . a p a c h e commons beanutils beanutils beanutils beanutils beanutils beanutils . BasicDynaBean ; . BasicDynaClass ; . BeanUtils ; . DynaBean ; . DynaProperty ; . WrapDynaBean ; public c l a s s TestDynaBean { . public s t a t i c void l u s t a B e a n ( ) { DynaBean dynaBean = new LazyDynaBean ( ) ; 49 Java programozói könyvtár Apache Commons - BeanUtils dynaBean . s e t ( " f o o " , " b a r " ) ; simple //å dynaBean . s e t ( " c u s t o m e r
" , " t i t l e " , "Mr" ) ; //å mapped dynaBean . s e t ( " c u s t o m e r " , " surname " , " Smith " ) ; //å mapped System . o u t p r i n t l n ( dynaBean g e t ( " f o o " ) ) ; Gyumolcs alma = new Gyumolcs ( ) ; Gyumolcs k o r t e = new Gyumolcs ( ) ; Gyumolcs s z i l v a = new Gyumolcs ( ) ; } . } dynaBean . s e t ( " a d d r e s s " , 0 , alma ) ; dynaBean . s e t ( " a d d r e s s " , 1 , k o r t e ) ; dynaBean . s e t ( " a d d r e s s " , 2 , s z i l v a ) ; // i n d e x e d // i n d e x e d // i n d e x e d Létezik egy LazyDynaMap osztály is, amit hasonlóan használhatunk (3-14. Programlista) Érdekességként megjegyezzük, hogy az utolsó sorban látható getMap() metódussal bármikor egy szabványos Map objektumot kérhetünk le ettől a DynaBean objektumtól. // 3−14. P r o g r a m l i s t a : DynaBean dynaBean = new LazyDynaMap ( ) ; DynaBean // c r e a t e å
dynaBean . s e t ( " f o o " , " b a r " ) ; // s i m p l e dynaBean . s e t ( " c u s t o m e r " , " t i t l e " , "Mr" ) ; // mapped dynaBean . s e t ( " a d d r e s s " , 0 , a d d r e s s L i n e 1 ) ; // i n d e x e d Map myMap = dynaBean . getMap ( ) Map // r e t r i e v e the å Biztos sok olvasónak eszébe jutott a kérdés, hogy egy létező Map objektumból vajon lehete DynaBean-t készíteni. A 3-15 Programlista pont ezt tartalmazza: // 3−15. P r o g r a m l i s t a : Map myMap = . // e x i s i t n g Map DynaBean dynaBean = new LazyDynaMap (myMap) ; Map i n DynaBean dynaBean . s e t ( " f o o " , " b a r " ) ; properties // wrap å // s e t å A 3-16. Programlista és 3-17 Programlista példák további használati lehetőségeket mutatnak, amiket az eddigiek alapján könnyen meg tudunk érteni, ezért nem is fűzünk magyarázatot hozzájuk. Természetesen mindkét lehetőség
célja, hogy egy DynaBean interface-szel rendelkező objektumhoz jussunk, amivel kezeljük a jellemzők értékeit. // 3−16. P r o g r a m l i s t a : MutableDynaClass d y n a C l a s s = new LazyDynaClass ( ) ; // c r e a t e DynaClass d y n a C l a s s . add ( " amount " , j a v a l a n g I n t e g e r c l a s s ) ; d y n a C l a s s . add ( " o r d e r s " , OrderBean [ ] c l a s s ) ; d y n a C l a s s . add ( " o r d e r s " , j a v a u t i l TreeMapp c l a s s ) ; // add p r o p e r t y // add i n d e x e d p r o p e r t y // add mapped p r o p e r t y DynaBean dynaBean = new LazyDynaBean ( d y n a C l a s s ) ; // Create DynaBean w i t h a s s o c i a t e d DynaClass // 3−17. P r o g r a m l i s t a : DynaBean dynaBean = new LazyDynaBean ( ) ; // Create LazyDynaBean MutableDynaClass d y n a C l a s s = ( MutableDynaClass ) dynaBean . g e t D y n a C l a s s ( ) ; // g e t DynaClass d y n a C l a s s . add ( " amount " , j
a v a l a n g I n t e g e r c l a s s ) ; d y n a C l a s s . add ( " myBeans " , myPackage MyBean [ ] c l a s s ) ; d y n a C l a s s . add ( "myMap" , j a v a u t i l TreeMapp c l a s s ) ; // add p r o p e r t y // add ’ array ’ i n d e x e d p r o p e r t y // add mapped p r o p e r t y JavaBean konverziós lehetőségek Egy bean jellemzőit írhatjuk, olvashatjuk és szükség esetén annak értékeit átalakíthatjuk. Sok esetben az a program, amelyik ezeket az érték konvertálásokat végzi szintén fekete doboz számunkra, bár ők is a getter és setter metódusokkal szerzik és írják az értékeket. Annak érdekében, hogy ezen metódusok által visszaadott értékeket mégis befolyásolni tudjuk, készíthetünk olyan objektumokat, amelyek a Con50 verter interface-t implementálják. Az alábbiakban 2 egyszerű Converter osztály mutatunk be: • MyStringConverter (3-18. Programlista) • MyLongConverter (3-19. Programlista) // 3−18.
P r o g r a m l i s t a : MyStringConverter j a v a package o r g . c s b e a n u t i l s ; import o r g . a p a c h e commons b e a n u t i l s C o n v e r t e r ; public c l a s s M y S t r i n g C o n v e r t e r implements C o n v e r t e r { public O b j e c t c o n v e r t ( C l a s s type , O b j e c t v a l u e ) Java programozói könyvtár { } } Apache Commons - BeanUtils i f ( v a l u e == n u l l ) { i f ( useDefault ) { return d e f a u l t V a l u e ; } else { throw new C o n v e r s i o n E x c e p t i o n ( "No␣ d e f a u l t ␣ v a l u e ␣å specified ") ; } } i f ( v a l u e == n u l l ) { return ( S t r i n g ) n u l l ; } else { return ( v a l u e . t o S t r i n g ( ) r e p l a c e A l l ( "ma" , " x " ) ) ; } // 3−19. P r o g r a m l i s t a : MyLongConverter j a v a package o r g . c s b e a n u t i l s ; import o r g . a p a c h e commons b e a n u t i l s å ConversionException ; import o r g . a p a c
h e commons b e a n u t i l s C o n v e r t e r ; public c l a s s MyLongConverter implements C o n v e r t e r { private Object d e f a u l t V a l u e ; p r i v a t e boolean u s e D e f a u l t ; public MyLongConverter ( ) { t h i s ( true , new Long ( 0 ) ) ; } } public MyLongConverter ( boolean u s e D e f a u l t , defaultValue ) { this . useDefault = useDefault ; this . defaultValue = defaultValue ; } public O b j e c t c o n v e r t ( C l a s s type , 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 Object å Object value ) { } i f ( v a l u e i n s t a n c e o f Long ) { return new Long ( ( ( Long ) v a l u e ) . l o n g V a l u e ( ) + å 1000) ; } else { try { return new Long (new Long ( v a l u e . t o S t r i n g ( ) ) å longValue ( ) + 1000) ; } catch ( E x c e p t i o n e ) { System . e r r p r i n t l n ( e ) ; i f ( useDefault ) { return d e f a u l t V a l u e ; } else { throw new C o n v e r s i o n E x c e
p t i o n ( e ) ; } } } A fenti 2 Converter class implementációja nem túl bonyolult. A ConverterTest (3-20 Programlista) bemutatja a MyStringConverter használatát. // 3 −20. P r o g r a m l i s t a : C o n v e r t e r T e s t j a v a package o r g . c s b e a n u t i l s ; import import import import o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons beanutils beanutils beanutils beanutils . BeanUtilsBean ; . ConvertUtilsBean ; . PropertyUtils ; . PropertyUtilsBean ; public c l a s s C o n v e r t e r T e s t { public s t a t i c void main ( S t r i n g [ ] a r g s ) throws E x c e p t i o n { Gyumolcs gyumolcs = new Gyumolcs ( ) ; gyumolcs . name = " A l s s s s s s m a " ; gyumolcs . p r i c e = 5 5 ; C o n v e r t U t i l s B e a n c o n v e r t U t i l s B e a n = new C o n v e r t U t i l s B e a n ( ) ; convertUtilsBean . d e r e g i s t e r ( String class ) ; c o n v e r t U t i l s B e a n . r e g i s t
e r (new M y S t r i n g C o n v e r t e r ( ) , S t r i n g c l a s s ) ; BeanUtilsBean b e a n U t i l s B e a n = new BeanUtilsBean ( c o n v e r t U t i l s B e a n , new P r o p e r t y U t i l s B e a n ( ) ) ; System . out p r i n t l n ( "By␣ P r o p e r t y U t i l s : ␣ " + P r o p e r t y U t i l s . g e t P r o p e r t y ( gyumolcs , "name" ) ) ; } } System . out p r i n t l n ( "By␣ B e a n U t i l s : ␣ " + b e a n U t i l s B e a n . g e t P r o p e r t y ( gyumolcs , "name" ) ) ; F u t á s i eredmény : By P r o p e r t y U t i l s : A l s s s s s s m a By B e a n U t i l s : A l s s s s s s x 51 Java programozói könyvtár A 14-16 sorok között létrehozunk egy gyumolcs objektumot. A 18-20 sorokban létrehozzuk a convertUtilsBean konvertáló objektumot, amiben az alapértelmezett String.class-hoz rendelt konvertert leregisztráljuk Ezután a mi MyStringConverter osztályunkhoz rendeljük az összes
String.class típushoz szükséges elérő konverziót A 22 sorban létrehozott beanUtilsBean objektum a BeanUtils osztály „objektumos” változata, ahol convertUtilsBean és PropertyUtilsBean objektumokat is összerendeljük. Ennyi előzetes beállítás után a 25. és 28 sor által használt gyumolcs bean jellemző lekérdezés alapvetően másképpen működik. Az első a már jól ismert statikus PropertyUtils class segítségével dolgozik, ez semmit sem tud arról, hogy van egy saját konverterünk, emiatt a tényleges Alssssssma értéket jeleníti meg. A 2 kiírás már az „objektumos” beanUtilsBean bean-t használja ugyanerre a feladatra, de itt a Stringre regisztráltunk egy sajátot, emiatt most az Alssssssx fog megjelenni, ugyanis az minden „ma” részletet „x ”-re cserél. Végül itt jegyezzük meg, hogy a BeanUtils statikus utility osztályokat (BeanUtils, ConvertUtils, PropertyUtils) és az ezekhez hasonló osztály példányokat (BeanUtilsBean,
ConvertUtilsBean, PropertyUtilsBean) is rendelkezésünkre bocsátja. ugyanazzal az API-val Ugyanakkor az „objektumos” változat több egyedi működést is tud hordozni, ahogy a konvertereknél láttuk is. A BeanUtils további lehetőségei Apache Commons - BeanUtils ties(Object dest, Object orig) az orig objektum minden olyan jellemzőjének az értékét másolja a dest objektumba, ahol a property nevek megegyeznek. Itt akármilyen 2 eltérő objektum lehet, ami nagy rugalmasságot biztosít A következő példánkban most egy ugyanolyan Gyumolcs típusú objektumba másoltunk, de ez lehetett volna akármilyen más class példány is: Gyumolcs g y u m o l c s 2 = new Gyumolcs ( ) ; B e a n U t i l s . c o p y P r o p e r t i e s ( gyumolcs2 , g y u m o l c s ) ; System . o u t p r i n t l n ( g y u m o l c s 2 name ) ; A populate(object, map) használatát a következő kis programtöredék mutatja: Map p = new HashMap ( ) ; p . put ( "name" , " K ö r t e
" ) ; B e a n U t i l s . p o p u l a t e ( gyumolcs , p ) ; System . o u t p r i n t l n ( g y u m o l c s name ) ; A Map objektum azon értékeit, ahol a kulcs neve egyezik a gyumolcs objektum property nevével, átmásolja az objektumba, így a képernyőre a Körte szó kerül kiírásra. A describe(Object)Map a paraméter objektum leírását egy Map-be teszi: Map p = new HashMap ( ) ; p = BeanUtils . d e s c r i b e ( gyumolcs ) ; System . o u t p r i n t l n ( p ) ; Eredmény : { propMap ={} , w e i g h t = 0 . 0 , p r i c e =55 , name=Körte , q u a l i t y å =n u l l , p r o p L i s t=n u l l , c l a s s=c l a s s o r g . c s å b e a n u t i l s . Gyumolcs , v i t a m i n=o r g c s b e a n u t i l s å Vitamin@a8c488 , p r o p s=n u l l } Kollekciók A BeanPropertyValueChangeClosure class használatát a következő példa szemlélteti: // c r e a t e t h e c l o s u r e BeanPropertyValueChangeClosure c l o s u r e = new B e a n P r o p e r t y V a l u e C h a
n g e C l o s u r e ( " a c t i v e E m p l o y e e "å , B o o l e a n .TRUE ) ; // update t h e C o l l e c t i o n C o l l e c t i o n U t i l s . forAllDo ( peopleCollection , closure ); Befejezésül nézzünk meg néhány hasznos leheA closure kód darab egy activeEmployee tőséget, az egyszerűség érdekében csak a BeanUtils statikus metódusain keresztül vizsgálva. A property-t (bármilyen objektumban is van) kécloneBean(Object)Object metódus egy tetsző- pes igazra állítani Használatát az utolsó sor muleges klón objektumot ad vissza A copyProper- tatja 52 Java programozói könyvtár 4. Apache Commons - Lang Apache Commons - Lang Az Apache Commons Lang könyvtári csomag létrehozását az motiválta, hogy a standard java.lang package tudását kiegészítse. A benne lévő eszközök nagyon általánosak, így a mindennapi programozói feladatokban sokat segíthetnek A project webhelye: http://commonsapacheorg/ proper/commons-lang/. A
bemutatott példák nagy része az API leírásból származik Az Apache Commons Lang jelenleg 2 fő változatban használatos, amiket a 2. és 3 verzió néven emlegetünk és nem keverednek egymással, mert különböző csomagokban vannak: • 2. verzió: orgapachecommonslang • 3. verzió: orgapachecommonslang3 A továbbiakban elsősorban a 3. verziót tekintjük az ismertetés alapjául, de megjegyezzük, hogy sok szoftver még a 2. verziót használja String műveletek - StringUtils Rész String lekérés S t r i n g U t i l s . l e f t P a d ( " bat " , 1) = " bat " S t r i n g U t i l s . l e f t P a d ( " b a t " , −1) = " b a t " StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils . . . . . . rightPad ( null , ∗) rightPad ( "" , 3) rightPad ( " bat " , 3) rightPad ( " bat " , 5) rightPad ( " bat " , 1) r i g h t P a d ( " b a t " , −1) = = = = = = null
"␣␣␣" " bat " " bat ␣␣" " bat " " bat " A következő 2 metódus működése hasonló, de megadhatjuk azt a karaktert is, ami ez esetben a szóköz helyett foglalja a helyet. l e f t P a d ( S t r i n g s t r , i n t s i z e , char padChar ) ; r i g h t P a d ( S t r i n g s t r , i n t s i z e , char padChar ) ; A mid() függvény igyekszik visszaadni len darab karakterből álló Stringet a kezdőpozíciótól mid ( S t r i n g s t r , i n t pos , i n t l e n ) ; o S t r = S t r i n g U t i l s . mid ( " 0 1 2 3 4 5 6 7 8 9 " , 3 , 3 ) ; // Eredmény (10 h o s s z ú S t r i n g ) : 345 A left() és right() metódusok a String bal, ilA könyvtár természetesen tartalmazza a letve jobb részét adják vissza annyi karakterben, klasszikus substring() metódust is, aminek a műamennyi a 2. paraméterben meg lett adva ködése megegyezik a String beépített lehetőségéS t r i n g U t i l s . l e f t ( null ,
∗) = null vel, de nem fut kivételre a szélsőséges esetekben: S t r i n g U t i l s . l e f t ( ∗ , −ve ) = "" StringUtils StringUtils StringUtils StringUtils . . . . left left left left ( "" , ∗) ( " abc " , 0 ) ( " abc " , 2 ) ( " abc " , 4 ) StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils . . . . . . right right right right right right ( null , ∗) ( ∗ , −ve ) ( "" , ∗) ( " abc " , 0 ) ( " abc " , 2 ) ( " abc " , 4 ) = = = = "" "" " ab " " abc " = = = = = = null "" "" "" " bc " " abc " // Amikor c s a k a s t a r t van megadva S t r i n g U t i l s . s u b s t r i n g ( null , ∗) = S t r i n g U t i l s . s u b s t r i n g ( "" , ∗) = S t r i n g U t i l s . s u b s t r i n g ( " abc " , 0 ) = S t r i n g U t i l s . s u b s t r
i n g ( " abc " , 2 ) = S t r i n g U t i l s . s u b s t r i n g ( " abc " , 4 ) = S t r i n g U t i l s . s u b s t r i n g ( " abc " , −2) = S t r i n g U t i l s . s u b s t r i n g ( " abc " , −4) = // S t a r t é s End i s van S t r i n g U t i l s . s u b s t r i n g ( null , S t r i n g U t i l s . s u b s t r i n g ( "" , ∗ S t r i n g U t i l s . s u b s t r i n g ( " abc " S t r i n g U t i l s . s u b s t r i n g ( " abc " S t r i n g U t i l s . s u b s t r i n g ( " abc " S t r i n g U t i l s . s u b s t r i n g ( " abc " S t r i n g U t i l s . s u b s t r i n g ( " abc " S t r i n g U t i l s . s u b s t r i n g ( " abc " S t r i n g U t i l s . s u b s t r i n g ( " abc " null "" " abc " "c" "" " bc " " abc " ∗ , ∗) , ∗) , 0 , 2) , 2 , 0) , 2 , 4) , 4 , 6) , 2 , 2) , −2,
−1) , −4, 2 ) = = = = = = = = = null "" ; " ab " "" "c" "" "" "b" " ab " A leftPad() egy olyan Stringet ad vissza, ami az 1. paraméterből úgy keletkezik, hogy a 2 paraméterben megadott számérték lesz a String hossza. Amennyiben az eredeti String ennél hosszabb, úgy azt balról szóközzel egészíti ki. A Van néhány további substring() mutáció is, rightPad () működése hasonló, de szükség esetén ezekről jó tudni. A substringBefore() metódus itt jobbról lesznek a szóközök hozzáragasztva. például visszaadja azt a rész Stringet, amit a 2. S t r i n g U t i l s . l e f t P a d ( null , ∗) = null paraméterben megadott szeparátor előtt talált: S t r i n g U t i l s . leftPad ( "" , 3) = "␣␣␣" S t r i n g U t i l s . l e f t P a d ( " bat " , 3) S t r i n g U t i l s . l e f t P a d ( " bat " , 5) = " bat " =
"␣␣ bat " S t r i n g U t i l s . s u b s t r i n g B e f o r e ( " a l m a f a " , " a f " ) = " alm " 53 Java programozói könyvtár Apache Commons - Lang A substringAfter() a szeparátor utáni String részletet szolgáltatja. A substringBeforeLast() és substringAfterLast() hasonló működéssel bír, de fel van készítve arra, hogy a szeparátor több alkalommal is előfordul és mindig az utolsó előfordulást fogja referenciaként tekinteni: Egy 4. paraméterben is meg lehet adni, hogy maximum hány cserét engedélyezünk. A következő példában az „a” 3 alkalommal lenne cserélve, de a megadott 2 érték csak a balról számított első kettőt engedi S t r i n g U t i l s . s u b s t r i n g B e f o r e L a s t ( " abcba " , "b" ) = " abc " S t r i n g U t i l s . s u b s t r i n g A f t e r L a s t ( " abcba " , "b" ) = " a " Amennyiben az
engedélyezett cserék száma 1, úgy erre a replaceOnce() függvény a legalkalmasabb, aminek nincs 4. paramétere, mert az konstans 1. A replaceEach() egy hatékonyságnövelő metódus, mert sokszor több String illeszkedést kell cserélni egy forrás Stringre: A substringBetween() a 2. paraméterben megadott String, mint 2 befoglaló közötti Stringet adja vissza. Létezik egy másik alakja is, ahol a nyitó és záró befoglaló Stringek különbözőek lehetnek. StringUtils null StringUtils StringUtils null StringUtils null StringUtils StringUtils abc " . s u b s t r i n g B e t w e e n ( null , ∗) = å . substringBetween ( "" , "" ) . substringBetween ( "" , " tag " ) = "" = å . substringBetween ( " tagabctag " , null ) = å . substringBetween ( " tagabctag " , "" ) = "" . s u b s t r i n g B e t w e e n ( " t a g a b c t a g " , " t a g " ) = "å
Az overlay() metódus 2 String egymást való takarásából (a 2. takarja az 1 String-et) adódó eredmény String-et ad vissza. Itt a 3 és 4 paraméterek a takarás kezdő és végző pozíciói S t r i n g U t i l s . o v e r l a y ( null , ∗ , ∗ , ∗) S t r i n g U t i l s . o v e r l a y ( " " , " abc " , 0 , 0 ) S t r i n g U t i l s . o v e r l a y ( " a b c d e f " , null , 2 , 4) S t r i n g U t i l s . overlay ( " abcdef " , "" , 2 , 4) S t r i n g U t i l s . overlay ( " abcdef " , "" , 4 , 2) S t r i n g U t i l s . overlay ( " abcdef " , " zzzz " , 2 , 4) abzzzzef " S t r i n g U t i l s . overlay ( " abcdef " , " zzzz " , 4 , 2) abzzzzef " S t r i n g U t i l s . o v e r l a y ( " a b c d e f " , " z z z z " , −1, 4 ) zzzzef " S t r i n g U t i l s . overlay ( " abcdef " , " zzzz " , 2 , 8) abzzzz
" S t r i n g U t i l s . o v e r l a y ( " a b c d e f " , " z z z z " , −2, −3) zzzzabcdef " S t r i n g U t i l s . overlay ( " abcdef " , " zzzz " , 8 , 10) abcdefzzzz " = = = = = = null " abc " " abef " " abef " " abef " "å = "å S t r i n g U t i l s . r e p l a c e ( " abaa " , " a " , " z " , 2 ) String = " zbza " replaceEach ( String text , String [ ] searchList , String [ ] replacementList ) Látható, hogy ekkor a kereső kifejezések és a csere értékek is egy-egy tömbben vannak. Nézzünk néhány példát! S t r i n g U t i l s . r e p l a c e E a c h ( null , ∗ , ∗) = null S t r i n g U t i l s . replaceEach ( "" , ∗ , ∗) = "" S t r i n g U t i l s . r e p l a c e E a c h ( " aba " , n u l l , n u l l ) = " aba " S t r i n g U t i l s . r e p l a c e E a c h (
" aba " , new S t r i n g [ 0 ] , n u l l ) = å " aba " S t r i n g U t i l s . r e p l a c e E a c h ( " aba " , n u l l , new S t r i n g [ 0 ] ) = å " aba " S t r i n g U t i l s . r e p l a c e E a c h ( " aba " , new S t r i n g [ ] { " a " } , n u l l å ) = " aba " S t r i n g U t i l s . r e p l a c e E a c h ( " aba " , new S t r i n g [ ] { " a " } , new å S t r i n g [ ] { " " } ) = "b" S t r i n g U t i l s . r e p l a c e E a c h ( " aba " , new S t r i n g [ ] { n u l l } , newå S t r i n g [ ] { " a " } ) = " aba " S t r i n g U t i l s . r e p l a c e E a c h ( " a b c d e " , new S t r i n g [ ] { " ab " , "då " } , new S t r i n g [ ] { "w" , " t " } ) = " wcte " S t r i n g U t i l s . r e p l a c e E a c h ( " a b c d e " , new S t r i n g [ ] { " ab
" , "då " } , new S t r i n g [ ] { "d" , " t " } ) = " d c t e " = "å = "å = "å = "å Egy String részlet cseréje Törlés a Stringből A remove() metódus törli a megadott karakter vagy String összes előfordulását. StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils . remove ( n u l l , ∗ ) . remove ( " " , ∗ ) . remove ( ∗ , n u l l ) . remove ( ∗ , " " ) . remove ( " queued " , " ue " ) . remove ( " queued " , " z z " ) = = = = = = null "" ∗ ∗ " qd " " queued " A cserére szolgáló replace() metódus is sokféle A removeEnd() csak a String végéről törli az megvalósításban áll a rendelkezésünkre, de ez a előfordulást, persze csak amennyiben ott megtaleggyakrabban használt alakja: lálható. Ellenkező esetben visszaadja az eredeti public s t a t i c S t r i n g r e p l
a c e ( S t r i n g t e x t , String searchString , forrás Stringet. String replacement ) A használatára egy példa: o S t r = S t r i n g U t i l s . r e p l a c e ( " aaabbaaa " , "bb " , " c c " ) ; // Eredmény aaaccaaa 54 S t r i n g U t i l s . removeEnd ( n u l l , ∗ ) = null S t r i n g U t i l s . removeEnd ( " " , ∗ ) = "" S t r i n g U t i l s . removeEnd ( ∗ , n u l l ) = ∗ S t r i n g U t i l s . removeEnd ( "www domain com" , " com " ) www. domain com" S t r i n g U t i l s . removeEnd ( "www domain com" , " com" ) www. domain " = "å = "å Java programozói könyvtár Apache Commons - Lang S t r i n g U t i l s . removeEnd ( "www domain com" , " domain " ) = "å www. domain com" S t r i n g U t i l s . removeEnd ( " abc " , " " ) = " abc " A removeEndIgnoreCase() hasonlót
csinál, de nem veszi figyelembe a kisbetű/nagybetű eltéréseket. A removeStart() és removeStartIgnoreCase() függvények a String elejével művelik mindezt. A trim() család a klasszikus String eleje és vége törlést valósítja meg, amikor az valamilyen vezérlő karakter (ASCII kód 5 32). S t r i n g U t i l s . t r i m ( " ␣ ␣ ␣ ␣ abc ␣ ␣ ␣ ␣ " ) = " abc " S t r i n g U t i l s . trim ( "␣␣␣␣␣" ) = "" paraméter a szétvágandó String, a 2. pedig a szeparátor. Az eredmény egy String tömbbe kerül S t r i n g U t i l s . s p l i t ( " aa bb c c " , cc " ] ’. ’) = [ " aa " , "bb " , "å Az 1 paraméteres split() a whitespace karakterek mentén vágja fel a String inputját. Lehetőség van egy 3 split() paraméter megadására is, ami az előállított tömb elemszámának a maximumát is figyelembe veszi: S t r i n g U t i l s . s p l i t ( " ab : cd
: e f " , " : " , 2 ) ef " ] = [ " ab " , " cd : å A trimToNull() üres String helyett null értéA splitByWholeSeparator() egy egész mintát ket ad vissza: képes elválasztóként használni: StringUtils StringUtils StringUtils StringUtils StringUtils . . . . . trimToNull ( null ) trimToNull ( "" ) trimToNull ( "␣␣␣␣␣" ) t r i m T o N u l l ( " abc " ) t r i m T o N u l l ( " ␣ ␣ ␣ ␣ abc ␣ ␣ ␣ ␣ " ) = = = = = null null null " abc " " abc " S t r i n g U t i l s . s p l i t B y W h o l e S e p a r a t o r ( " ab−!−cd −!− e f " , "−!−å " ) = [ " ab " , " cd " , " e f " ] A többi split változatot az API leírásból javaA trimToEmpty() pedig mindig üres Strin- soljuk megnézni, azokra ritkábban lehet szükség. get ad vissza, amikor a fentiek null -t adnának. A strip() család hasonló a
trim()-hez, de a ve- Középre igazítás zérlőkarakterek helyett a whitespace-t figyeli. A A center() metódusnak több változata is van, stripAll() String tömbön is elvégzi a feladatot. A de mindegyiknek az a célja, hogy egy szöveget stripAccents() érdekes lehetőség, mert képes egy középre igazítson. olyan Stringet visszaadni, amiben már nincsenek S t r i n g oStr = null ; o S t r = S t r i n g U t i l s . c e n t e r ( " aaa " , 1 0 ) ; ékezetes karakterek (diacritics=ékezetek): System . o u t p r i n t l n ( o S t r ) ; StringUtils . stripAccents (" é c l a i r ") = " e c l a i r " String készítés ismétléssel A repeat() az 1. paraméterben megadott mintából olyan eredmény karaktersorozatot készít, ami annak a 2. paraméterben megadott számú ismétlésével áll elő. A 4 példa azt is megmutatja, hogy amennyiben a 3 paraméteres változatot használjuk, úgy szeparátor is megadható StringUtils StringUtils StringUtils
StringUtils StringUtils . . . . . r e p e a t ( " ab " , 2 ) = r e p e a t ( null , 2) = repeat ( "" , 0) = repeat ( "" , 2) = repeat ( "?" , " , ␣" , " abab " null "" "" 3) = " ? , ␣ ? , ␣?" String darabokra vágása // Eredmény (10 h o s s z ú S t r i n g ) : aaa Megadhatjuk 3. paraméterként a kitöltő karaktert is: S t r i n g oStr = null ; o S t r = S t r i n g U t i l s . c e n t e r ( " aaa " , 1 0 , "@" ) ; System . o u t p r i n t l n ( o S t r ) ; o S t r = S t r i n g U t i l s . c e n t e r ( " aaa " , 1 0 , "@X" ) ; System . o u t p r i n t l n ( o S t r ) ; // Eredmény : @@@aaa@@@@ @X@aaa@X@X String megfordítása A reverse() metódus az input Stringet visszafelé írva adja vissza: S t r i n g U t i l s . r e v e r s e ( null ) = null S t r i n g U t i l s . r e v e r s e ( "" ) = "" S t r i n g U t i l
s . r e v e r s e ( " bat " ) = " tab " Egy forrás String több darabra vágása a split() Ennek létezik egy reverseDelimited() nevű családdal valósítható meg. Alapesetben az 1 változata, ahol egy szeparátort is meg lehet adni, 55 Java programozói könyvtár Apache Commons - Lang a Stringet pedig a csoportok visszafelé olvasásával adja vissza: S t r i n g U t i l s . r e v e r s e D e l i m i t e d ( " aa b2 c 1 " , StringUtils StringUtils StringUtils StringUtils StringUtils ’. ’); // Eredmény : c 1 . b2 aa Az equals() 2 String egyenlőségét vizsgálja: . . . . . e q u a l s ( null , e q u a l s ( null , e q u a l s ( " abc " e q u a l s ( " abc " e q u a l s ( " abc " . . . . . s t a r t s W i t h ( null , null ) s t a r t s W i t h ( n u l l , " abc " ) startsWith ( " abcdef " , null ) s t a r t s W i t h ( " a b c d e f " , " abc " ) s t a r t s W
i t h ( "ABCDEF" , " abc " ) = = = = = true false false true false Ezen 2 alap metódusnak lézetik néhány változata: Összehasonlítás StringUtils StringUtils StringUtils StringUtils StringUtils S t r i n g U t i l s . endsWith ( " a b c d e f " , " d e f " ) = true S t r i n g U t i l s . endsWith ( "ABCDEF" , " d e f " ) = f a l s e S t r i n g U t i l s . endsWith ( "ABCDEF" , " c d e " ) = f a l s e null ) " abc " ) , null ) , " abc " ) , "ABC" ) = = = = = true false false true false • startsWithIgnoreCase() • endWithIgnoreCase() • endsWithAny() Ennek létezik equalsIgnoreCase() alakja is. Megvizsgálhatjuk, hogy egy String bizonyos ka• startWithAny() raktersorozattal kezdődik vagy végződik. Erre szolgál az endsWith() és startsWith() metódu- Az első két függvény egyértelmű. Az „any” metódusok azt vizsgálják, hogy a tömbben megadott
sok: esetek valamelyikével kezdődik vagy végződik-e S t r i n g U t i l s . endsWith ( n u l l , n u l l ) = true S t r i n g U t i l s . endsWith ( n u l l , " d e f " ) = false S t r i n g U t i l s . endsWith ( " a b c d e f " , n u l l ) = f a l s e a String. StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils . endsWithAny ( null , n u l l ) = false . endsWithAny ( null , new S t r i n g [ ] { " abc " } ) = f a l s e . endsWithAny ( " abcxyz " , n u l l ) = false . endsWithAny ( " abcxyz " , new S t r i n g [ ] { " " } ) = true . endsWithAny ( " abcxyz " , new S t r i n g [ ] { " xyz " } ) = true . endsWithAny ( " abcxyz " , new S t r i n g [ ] { null , " xyz " , " abc " } ) = true A getCommonPrefix() egy érdekes lehetőség, elemét és amennyiben van, úgy visszaadja a kömert képes megvizsgálni egy String tömb összes zös prefixüket.
StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils 56 . getCommonPrefix ( n u l l ) = " " . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] . getCommonPrefix (new S t r i n g [ ] {}) = "" { " abc " } ) = " abc " { null , n u l l } ) = " " {"" ,
"" }) = "" {"" , null }) = "" { " abc " , null , n u l l } ) = " " { null , null , " abc " } ) = " " { " " , " abc " } ) = " " { " abc " , " " } ) = " " { " abc " , " abc " } ) = " abc " { " abc " , " a " } ) = " a " { " ab " , " abxyz " } ) = " ab " { " abcde " , " abxyz " } ) = " ab " { " abcde " , " xyz " } ) = " " { " xyz " , " abcde " } ) = " " { " i ␣am␣ a ␣ machine " , " i ␣am␣ a ␣ r o b o t " } ) = " i ␣am␣ a ␣ " Java programozói könyvtár Apache Commons - Lang Rész String tartalmazás hogy a keresett karaktersorozat hányadik előfordulásának pozíciója érdekel bennünket: A contains() megvizsgálja,
hogy az első String tartalmazza-e a másodikat: boolean b = S t r i n g U t i l s . c o n t a i n s ( " Almafa " , "ma" ) ; System . o u t p r i n t l n ( b ) ; // Eredmény : t r u e StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils . . . . . . . . . . . o r d i n a l I n d e x O f ( null , ∗ , ∗) o r d i n a l I n d e x O f ( ∗ , null , ∗) ordinalIndexOf ( "" , "" , ∗) o r d i n a l I n d e x O f ( " aabaabaa " , o r d i n a l I n d e x O f ( " aabaabaa " , o r d i n a l I n d e x O f ( " aabaabaa " , o r d i n a l I n d e x O f ( " aabaabaa " , o r d i n a l I n d e x O f ( " aabaabaa " , o r d i n a l I n d e x O f ( " aabaabaa " , o r d i n a l I n d e x O f ( " aabaabaa " , o r d i n a l I n d e x O f ( " aabaabaa " , = = = "a" , 1) = "a" , 2) =
"b" , 1 ) = "b" , 2 ) = " ab " , 1 ) = " ab " , 2 ) = "" , 1) = "" , 2) = −1 −1 0 0 1 2 5 1 4 0 0 A containsIgnoreCase() metódus változat figyelmen kívül hagyja a kisbetű/nagybetű különbségeket. A containsWhitespace() metódus Az indexOfAny() több lehetséges keresendő megvizsgálja, hogy egyetlen String paramétere érték közül bármelyikre való találat indexét adja tartalmaz-e whitespace karaktert. A containvissza: sAny() kicsit rugalmasabb, mert fel lehet soEz 3 l e s z , mert b vagy y a k e r e s e n d ő : rolni azokat az eseteket, amiknek a tartalmazá- // i n t p o s = S t r i n g U t i l s . indexOfAny ( " z z a b y y c d x x " , ’ b ’ , ’ y ’ å ); sát vizsgáljuk: boolean b ; b = S t r i n g U t i l s . c o n t a i n s A n y ( "Alma␣ é s ␣ k ö r t e " , ’t ’ ); System . o u t p r i n t l n ( b ) ; ’a ’ , ’ ö ’ ,å // vagy : boolean b = S t r i
n g U t i l s . c o n t a i n s A n y ( "Alma␣ é s ␣ k ö r t e " , "å aöt " ) ; // Eredmény : // S t r i n g −r e S t r i n g U t i l s . indexOfAny ( n u l l , ∗ ) S t r i n g U t i l s . indexOfAny ( " " , ∗ ) S t r i n g U t i l s . indexOfAny ( ∗ , n u l l ) S t r i n g U t i l s . indexOfAny ( ∗ , " " ) S t r i n g U t i l s . indexOfAny ( " z z a b y y c d x x " , " z a " ) S t r i n g U t i l s . indexOfAny ( " z z a b y y c d x x " , " by " ) S t r i n g U t i l s . indexOfAny ( " aba " , " z " ) = = = = = = = −1 −1 −1 −1 0 3 −1 true Az indexOfAnyBut() azt a pozíciót keresi, Ennek az ellentéte a containsNone() metó- ami először nem illeszkedik balról a mintára. dus, aminek ugyanilyen a paraméterezése. A S t r i n g U t i l s indexOfAnyBut ( " z z a b y y c d x x " , new char [ ] { ’ zå ) = 3 containsOnly() azért fontos,
mert ezzel azt tud- S t r i n’g,U t’ ial’s}. indexOfAnyBut ( " aba " , new char [ ] { ’ z ’ } ) å = 0 juk levizsgáltatni, hogy az 1. paraméter csak S t r i n g U t i l s indexOfAnyBut ( " aba " , new char [ ] { ’ a ’ , ’ b ’ }å ) = −1 azokat a karaktereket tartalmazza-e, amit a 2. S t r i n g U t i l s . indexOfAnyBut ( " z z a b y y c d x x " , " z a " ) = 3 paraméterben megadtunk. S t r i n g U t i l s . indexOfAnyBut ( " z z a b y y c d x x " , " " ) = −1 S t r i n g U t i l s . indexOfAnyBut ( " aba " , " ab " ) Keresés A String class is tartalmaz egy indexOf() metódust, ezért felmerülhet a kérdés, hogy a StringUtils ugyanilyen nevű módszere miben tud többet. Azt mindjárt az elején fontos megállapítani, hogy ez a változat amikor csak lehet delegálja tovább a feladatot a String.indexOf(String, int) metódusnak. // j a v a . l a n g N u l l P o i n t e r E x c
e p t i o n i n t p o s = " alma " . i n d e x O f ( n u l l ) ; = −1 Az indexOfDifference() a paraméterében lévő 2 Stringre visszaadja azt a pozíciót, ahol a prefixük először nem egyeznek meg egymással. Az indexOfIgnoreCase() nem tesz különbséget kis és nagybetű között A lastIndexOf() az utolsó előfordulás indexét adja vissza: i n t p o s = S t r i n g U t i l s . l a s t I n d e x O f ( " sbxabaddxaddd " , " xaå ") ; // Eredmény : 8 lesz : // J ó l működik n u l l −ra i s , −1 l e s z : S t r i n g U t i l s . i n d e x O f ( " alma " , n u l l ) ; Az indexOf() függvénynek létezik egy olyan változata is, ahol a keresés kezdőpozícióját is meg lehet adni. Az ordinalIndexOf() által megvalósított keresésnél még azt is meg lehet adni, Itt jegyezzük meg, hogy ezek a metódusok egy CharSequence interface-szel rendelkező objektumon tudnak dolgozni, ilyen többek között a jól ismert String is. A
már ismert lastIndexOfAny(), lastIndexOfIgnoreCase() és lastOrdinalIndexOf() változatok itt is elérhetőek 57 Java programozói könyvtár Apache Commons - Lang Logikai lekérdezések Az is.() kezdetű metódusok különféle kérdéseket tesznek fel a paraméterül kapott String-nek, amikre igen/nem válasz érkezhet. Az isAllLowerCase() akkor és csak akkor (továbbiakban: a.csa), ha minden karaktere kisbetű StringUtils StringUtils StringUtils StringUtils StringUtils . . . . . isAllLowerCase ( null ) = false isAllLowerCase ( "" ) = false isAllLowerCase ( "␣␣" ) = false i s A l l L o w e r C a s e ( " abc " ) = true i s A l l L o w e r C a s e ( "abC" ) = f a l s e S t r i n g U t i l s . isEmpty ( " ␣ " ) = false S t r i n g U t i l s . isEmpty ( " bob " ) = false S t r i n g U t i l s . isEmpty ( " ␣ ␣ bob ␣ ␣ " ) = f a l s e StringUtils StringUtils StringUtils StringUtils StringUtils
. isNotEmpty ( n u l l ) . isNotEmpty ( " " ) . isNotEmpty ( " ␣ " ) . isNotEmpty ( " bob " ) . isNotEmpty ( " ␣ ␣ bob ␣ ␣ " ) = = = = = false false true true true Az isNumeric() természetesen azt dönti el, hogy egy Stringben csak számjegyek vannak-e: StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils . . . . . . . . isNumeric ( null ) isNumeric ( "" ) isNumeric ( "␣␣" ) i s N u m e r i c ( " 123 " ) i s N u m e r i c ( " 12 ␣ 3 " ) i s N u m e r i c ( " ab2c " ) i s N u m e r i c ( "12−3" ) isNumeric ( " 12.3 " ) = = = = = = = = false false false true false false false false Az isAllUpperCase() a.csa igaz, ha minden karaktere nagybetű. Az isAlpha() acsa igaz, Az isNumericSpace() azt is megengedi, hogy ha legalább 1 hosszú és minden karaktere betű. Az isAlphanumeric() a.csa igaz, ha legalább 1 a
String üres legyen vagy szóközt tartalmazhosszú és minden karaktere számjegy vagy betű: zon Az isWhitespace() acsa igaz, ha az csak whitespace karaktereket tartalmaz vagy empty S t r i n g U t i l s . isAlphanumeric ( null ) = false S t r i n g U t i l s . isAlphanumeric ( "" ) = false a String. A null értékre hamis S t r i n g U t i l s . isAlphanumeric ( "␣␣" ) = false StringUtils StringUtils StringUtils StringUtils . . . . i s A l p h a n u m e r i c ( " abc " ) i s A l p h a n u m e r i c ( " ab ␣ c " ) i s A l p h a n u m e r i c ( " ab2c " ) i s A l p h a n u m e r i c ( " ab−c " ) = = = = true false true false Összekapcsolás Az isAlphanumericSpace() használati eseteit Szintén alapfeladat néhány String vagy karakter a következő tesztesetekből láthatjuk: összeragasztása, amit a join() metódus családdal S t r i n g U t i l s . isAlphanumericSpace ( null ) = false tudunk különféle
inputokra elvégezni. Látható, S t r i n g U t i l s . isAlphanumericSpace ( "" ) = true S t r i n g U t i l s . isAlphanumericSpace ( "␣␣" ) = true hogy az összeragasztandó értékek vagy változó S t r i n g U t i l s . i s A l p h a n u m e r i c S p a c e ( " abc " ) = true S t r i n g U t i l s . i s A l p h a n u m e r i c S p a c e ( " ab ␣ c " ) = true számú paraméterként vagy egy tömbben lehetS t r i n g U t i l s . i s A l p h a n u m e r i c S p a c e ( " ab2c " ) = true S t r i n g U t i l s . i s A l p h a n u m e r i c S p a c e ( " ab−c " ) = f a l s e nek. Amennyiben a tömbös verziónál megadunk Az isAlphaSpace() a.csa igaz, ha betűt vagy egy 2 paramétert is, úgy az egy elválasztóként szóközt tartalmaz és nem null. Itt az üres String fog funkcionálni és felsorolásként lesz a join eredtrue értéket ad vissza Az isAsciiPrintable() ménye megvalósulva Lehetőség van ekkor egy
3 a.csa fog igazzal visszatérni, ha a String nyom- és 4 paraméter megadására is, amikor azok a tatható és csak eredeti ASCII karaktereket tar- tömb figyelembe vett kezdő és záró elemét adják talmaz. Aki ránéz az isBlank() és isNotBlank meg " a " , "b" , " c " ) ; = abc metódusokhoz adott példákra, egyből rájön a SS tt rr ii nn gg UU tt ii ll ss . jj oo ii nn ((new S t r i n g [ ] { " a " , " á " , "b" } ) ; = aáb működésére: S t r i n g U t i l s . j o i n (new S t r i n g [ ] { " a " , "b" , " c " } , " ; " ) ; =å StringUtils StringUtils StringUtils StringUtils StringUtils . . . . . isBlank ( null ) isBlank ( "" ) isBlank ( "␣" ) i s B l a n k ( " bob " ) i s B l a n k ( " ␣ ␣ bob ␣ ␣ " ) = = = = = StringUtils StringUtils StringUtils StringUtils StringUtils . . . . . isNotBlank ( null ) isNotBlank (
"" ) isNotBlank ( "␣" ) i s N o t B l a n k ( " bob " ) i s N o t B l a n k ( " ␣ ␣ bob ␣ ␣ " ) true true true false false = = = = = false false false true true a;b;c A join() első paramétere egy Iterator<?> vagy Iterable<?> is lehet. Alapértelmezett értékek üres Stringre A defaultIfBlank() az 1. paraméter vizsgálata Ugyanezt mondhatjuk el az isEmpty() és isalapján visszaadja a 2. paramétert, amennyiben NotEmpty() esetére is: az „ „ , üres vagy null. Minden más esetben az 1 S t r i n g U t i l s . isEmpty ( n u l l ) = true S t r i n g U t i l s . isEmpty ( " " ) = true paraméterben kapott String-et kapjuk vissza. 58 Java programozói könyvtár StringUtils StringUtils StringUtils StringUtils StringUtils . . . . . Apache Commons - Lang d e f a u l t I f B l a n k ( n u l l , "NULL" ) d e f a u l t I f B l a n k ( " " , "NULL" ) d e f a u l t I f B l a n
k ( " ␣ " , "NULL" ) d e f a u l t I f B l a n k ( " b a t " , "NULL" ) d e f a u l t I f B l a n k ( "" , null ) = = = = = "NULL" "NULL" "NULL" " bat " null Amennyiben az abbreviate() 3 paraméteres változatát adjuk meg, úgy ezzel azt határozzuk meg, hogy honnan akarjuk a Stringet látni, előtte és utána . lesz: Az is lehet, hogy csak az üres (empty) és oStr = S t r i n g U t i l s . abbreviate ( str , 5 , 15) ; null értékek alapján szeretnénk a fenti műköEkkor az eredmény ez lesz: .a fa alat Védést, erre szolgál a defaultIfEmpty() Valószínű, hogy legtöbbet a defaultString() metódus lesz gül létezik az abbreviateMiddle() metódus, amelyik a String közepét cseréli a 2. paraméterhasználva, ami csak a null értéket vizsgálja: ben megadott szövegre, miközben az eredmény S t r i n g U t i l s . d e f a u l t S t r i n g ( n u l l , "NULL" ) =
"NULL" S t r i n g U t i l s . d e f a u l t S t r i n g ( " " , "NULL" ) = "" String hossza a 3. paraméterrel van specifikálva: S t r i n g U t i l s . d e f a u l t S t r i n g ( " b a t " , "NULL" ) = " b a t " Ennek létezik 1 paraméteres változata is, amikor null esetén mindig üres String az eredmény: S t r i n g s t r="Alma␣ a ␣ f a ␣ a l a t t , ␣ n y á r i ␣ p i r o s ␣ alma . " ; S t r i n g oStr = null ; o S t r=S t r i n g U t i l s . a b b r e v i a t e M i d d l e ( s t r , " ␣ @csere@ ␣ " , å 15) ; // Eredmény : Alm @csere@ ma . S t r i n g U t i l s . d e f a u l t S t r i n g ( null ) = "" S t r i n g U t i l s . d e f a u l t S t r i n g ( " bat " ) = " bat " Az újsor karakter törlése Törlés A deleteWhitespace() metódus törli a fehér szóközöket a String-ből, ahogy a példa mutatja: StringUtils StringUtils
StringUtils StringUtils . . . . deleteWhitespace ( null ) deleteWhitespace ( "" ) d e l e t e W h i t e s p a c e ( " abc " ) d e l e t e W h i t e s p a c e ( " ␣ ␣ ␣ ab ␣ ␣ c ␣ ␣ " ) = = = = null "" " abc " " abc " A chomp() segítségével törölhetjük a String végéről az újsor jellegű karaktereket: // Újsor k a r a k t e r e k : // " " , " r " , or " r " S t r i n g oStr = null ; o S t r = S t r i n g U t i l s . chomp ( " alma " ) ; Az utolsó karakter törlése Előfordulás számlálás A countMatches() visszaadja, hogy az 1. paraméterben hány alkalommal fordult elő a 2 paraméter StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils . countMatches ( null , ∗) . countMatches ( "" , ∗) . c o u n t M a t c h e s ( " abba " , n u l l ) . c o u n t M a t c h e s ( " abba " , " "
) . c o u n t M a t c h e s ( " abba " , " a " ) . c o u n t M a t c h e s ( " abba " , " ab " ) . c o u n t M a t c h e s ( " abba " , " xxx " ) = = = = = = = 0 0 0 0 2 1 0 A chop() eltávolítja a String utolsó karakterét, ahogy ez a példa is mutatja: S t r i n g oStr = null ; o S t r = S t r i n g U t i l s . chop ( " alma " ) ; System . o u t p r i n t l n ( o S t r ) ; // Eredmény : alm Nagy és kisbetű A capitalize() metódus egy String első karakterét nagybetűre alakítja. Rövidítés o S t r = S t r i n g U t i l s . c a p i t a l i z e ( " alma ␣ a ␣ f a ␣ a l a t t " ) ; System . o u t p r i n t l n ( o S t r ) ; Az abbreviate() metódus képes egy Stringet lerövidíteni, a folytatását . -al jelölni: // Eredmény : Alma a f a a l a t t S t r i n g s t r="Alma␣ a ␣ f a ␣ a l a t t , ␣ n y á r i ␣ p i r o s ␣ alma . " ; String oStr = S t r i n g
U t i l s . abbreviate ( str , 10) ; System . o u t p r i n t l n ( o S t r ) ; // Eredmény : Alma a . A swapCase() a kisbetűt nagyra, a nagyot kicsire konvertálva adja vissza a forrás Stringet. Az uncapitalize() a fentebb bemutatott capitalize() ellentétes párja. 59 Java programozói könyvtár Apache Commons - Lang Az indexOfDifference() metódus azt a pozíciót adja vissza, ahol a paraméter két darab Ezt a műveletet a difference() metódus képes el- String-je elkezd különbözni. Létezik egy olyan végezni: függvény, ami visszaadja azt a számot, hogy S t r i n g U t i l s . d i f f e r e n c e ( null , null ) = null az egyik String-ben mennyi karakter változtatás S t r i n g U t i l s . d i f f e r e n c e ( "" , "" ) = "" S t r i n g U t i l s . d i f f e r e n c e ( " " , " abc " ) = " abc " (törlés, beszúrás, helyettesítés) lenne szükséges, S t r i n g U t i l s . d i f f e r e n c e (
" abc " , " " ) = " " S t r i n g U t i l s . d i f f e r e n c e ( " abc " , " abc " ) = " " hogy visszakapjuk a másik Stringet: getLevenshS t r i n g U t i l s . d i f f e r e n c e ( " ab " , " abxyz " ) = " xyz " S t r i n g U t i l s . d i f f e r e n c e ( " a b c d e " , " abxyz " ) = " xyz " teinDistance(). Nézzünk erre is példákat! S t r i n g U t i l s . d i f f e r e n c e ( " a b c d e " , " xyz " ) = " xyz " Két String különbsége StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils StringUtils . . . . . . . . . . . g e t L e v e n s h t e i n D i s t a n c e ( null , ∗ ) g e t L e v e n s h t e i n D i s t a n c e ( ∗ , null ) getLevenshteinDistance ( "" , "" ) getLevenshteinDistance ( "" , "a" ) g e t L
e v e n s h t e i n D i s t a n c e ( " aaapppp " , " " ) getLevenshteinDistance (" frog " , " fog ") g e t L e v e n s h t e i n D i s t a n c e ( " f l y " , " ant " ) g e t L e v e n s h t e i n D i s t a n c e ( " e l e p h a n t " , " hippo " ) g e t L e v e n s h t e i n D i s t a n c e ( " hippo " , " e l e p h a n t " ) g e t L e v e n s h t e i n D i s t a n c e ( " hippo " , " z z z z z z z z " ) getLevenshteinDistance (" hello " , " hallo ") Számok kezelése - NumberUtils String konvertálása szám osztályra A NumberUtils osztály create.() metódusai egy Stringet kapnak és visszaadnak egy olyan objektumot, ami ez alapján hozható létre. Az alábbiakban felsoroljuk ezeket a metódusokat: public public public public public public s t a t i c BigDecimal c reateBig Decimal ( S t r i n g static BigInteger createBigInteger ( String s
t a t i c Double c r e a t e D o u b l e ( S t r i n g s t r ) static Integer createInteger ( String str ) s t a t i c Long c r e a t e L o n g ( S t r i n g s t r ) static Float createFloat ( String s t r ) str ) str ) public s t a t i c Number c r e a t e N u m b e r ( S t r i n g s t r ) throws å NumberFormatException = = = = = = = = = = = IllegalArgumentException IllegalArgumentException 0 1 7 1 3 7 7 8 1 public s t a t i c double t o D o u b l e ( S t r i n g s t r ) public s t a t i c double t o D o u b l e ( S t r i n g s t r , double å defaultValue ) public s t a t i c f l o a t t o F l o a t ( S t r i n g s t r ) public s t a t i c f l o a t t o F l o a t ( S t r i n g s t r , f l o a t å defaultValue ) public s t a t i c short t o S h o r t ( S t r i n g s t r ) public s t a t i c short t o S h o r t ( S t r i n g s t r , short å defaultValue ) public s t a t i c byte t o B y t e ( S t r i n g s t r ) public s t a t i c byte t o B y t e ( S t r i n g s t r , byte å
defaultValue ) Szélsőérték visszaadása A max() és min() metódusok inputként valamilyen számtömböt kapnak és a tömb elemtípusának megfelelő értéket adnak vissza. A java.langNumber ősosztálya az összes Logikai lekérdezések megelőző class-nak. Az isDigits() leellenőrzi, hogy a paraméterül kapott String csak számjegyet tartalmaz-e. A null A to.() metódus család skalár számokat ad és az üres String is hamissal tér vissza Az isvissza a paraméterben megadott String alapján Number() azt nézi meg, hogy a forrás String Amennyiben megadunk egy 2. paramétert is, átalakítható-e egy Java Number objektummá úgy az egy alapértelmezett érték arra az esetre, ha a konverzió nem lett sikeres. Nézzük őket! A dátum és idő kezelése String konvertálása szám skalárra public s t a t i c i n t t o I n t ( S t r i n g s t r ) public s t a t i c i n t t o I n t ( S t r i n g s t r , i n t d e f a u l t V a l u e ) public s t a t i c long toLong ( S t r
i n g s t r ) public s t a t i c long toLong ( S t r i n g s t r , long å defaultValue ) 60 A Java nyelv beépített Date és Calendar osztályai mutatnak némi hiányosságot. Az ebben a Java programozói könyvtár Apache Commons - Lang pontban bemutatott lehetőségek előrelépést jeA következőkben csak megadjuk a többi halentenek ebben, de az igazi megoldást a követ- sonló metódus deklarációját: kező fejezetben bemutatott Joda.org könyvtár public s t a t i c Date addYears ( Date d a t e , i n t amount ) ; public s t a t i c Date addMonths ( Date d a t e , i n t amount ) ; fogja jelenteni. A Java környezet 2 fontos osz- public s t a t i c Date addWeeks ( Date d a t e , i n t amount ) ; public s t a t i c Date addHours ( Date d a t e , i n t amount ) ; tályt tartalmaz a dátumokkal kapcsolatosan: public s t a t i c Date addMinutes ( Date d a t e , i n t amount ) ; • java.utilCalendar (java.utilGregorianCalendar ) • java.utilDate public s t a t i c Date
a d d S e c o n d s ( Date d a t e , i n t amount ) ; public s t a t i c Date a d d M i l l i s e c o n d s ( Date d a t e , i n t å amount ) ; Dátumok beállítása Ezekről többet a következő cikkben írunk. Az A dátumok beállítása azt jelenti, hogy egy inorgapachecommonslang3time csomag a követ- put Date objektum valamelyik dátum kompokező osztályokkal segíti a programozót: nensét átállítjuk és az így kialakított dátumot adjuk vissza, miközben az eredeti dátum ettől • DateUtils: Közhasznú Date és Calendar nem változik meg. A lenti példában például a rutinok pillanatnyi időponthoz rendelt Date objektum • DateFormatUtils, DurationFormatUtils és hónapját decemberre állítjuk: FastDateFormat: A dátum Stringgé alakításának támogatása • StopWatch: Időzítés kezelés támogatása A DateUtils néhány fontos előre felvett konstanst is tartalmaz, például az 1 napra eső milliszekundumok száma. A dátumok növelése és
csökkentése A következő kód a tz változóban egy budapesti időzónát reprezentál, a c Calendar objektumot ezzel hoztuk létre. A d a Calendar objektumtól lekért időpillanatot reprezentálja, gyakorlatilag egy olyan eltárolására képes Date objektum. A DateUtils osztály addDays() metódusa egy dátumhoz napokat tud hozzáadni, ami lehet negatív szám is. Esetünkben most 3 nappal növeltük a dátumot. TimeZone t z = TimeZone . getTimeZone ( " Europe / Budapest " ) ; Calendar c = GregorianCalendar . g e t I n s t a n c e ( tz ) ; SimpleDateFormat d t = new SimpleDateFormat ( " yyyy−MM−ddå ␣HH:mm: s s " ) ; dt . setTimeZone ( t z ) ; Date d = c . getTime ( ) ; // Eredmény : 2013−05−29 1 7 : 5 9 : 2 2 System . o u t p r i n t l n ( d t f o r m a t ( d ) ) ; d = D a t e U t i l s . addDays ( d , 3 ) ; // Eredmény : 2013−06−01 1 7 : 5 9 : 2 2 System . o u t p r i n t l n ( d t f o r m a t ( d ) ) ; TimeZone t z = TimeZone .
getTimeZone ( " Europe / Budapest " ) ; Calendar c = GregorianCalendar . g e t I n s t a n c e ( tz ) ; SimpleDateFormat d t = new SimpleDateFormat ( " yyyy−MM−ddå ␣HH:mm: s s " ) ; dt . setTimeZone ( t z ) ; Date d = c . getTime ( ) ; // Eredmény : 2013−05−30 0 8 : 3 4 : 4 9 System . o u t p r i n t l n ( d t f o r m a t ( d ) ) ; d = D a t e U t i l s . setMonths ( d , 11) ; // Eredmény : 2013−12−27 0 7 : 3 4 : 4 9 System . o u t p r i n t l n ( d t f o r m a t ( d ) ) ; A további beállító metódusok a következőek: public s t a t i c public s t a t i c public s t a t i c public s t a t i c public s t a t i c public s t a t i c amount ) ; Date Date Date Date Date Date s e t Y e a r s ( Date d a t e , i n t amount ) ; s e t D a y s ( Date d a t e , i n t amount ) ; s e t H o u r s ( Date d a t e , i n t amount ) ; s e t M i n u t e s ( Date d a t e , i n t amount ) ; s e t S e c o n d s ( Date d a t e , i n t amount ) ; s e t M i l l i
s e c o n d s ( Date d a t e , i n t å Szövegből dátum A parseDate() és parseDateStrictly() metódusok egy dátum és egy minta String alapján előállítanak egy Date objektumot. Az alábbi példából láthatjuk, hogy több mintát is megadhatunk, a metódus végigpróbálja az összeset, hogy sikerrel járjon. S t r i n g [ ] p a t t e r n = new S t r i n g [ ] { " yyyy−MM−dd ␣HH:mm: s s " , " yyyy−MM−dd " }; Date pd = D a t e U t i l s . p a r s e D a t e ( " 2015−03−01 ␣ 0 8 : 0 7 : 0 2 " , å pattern ) ; 61 Java programozói könyvtár Apache Commons - Lang A parseDateStrictly() gyakorlatilag ugyanezt csinálja, de az elemzésnél szigorúbb, azaz egyegy résznél nem próbálja kitalálni a jelentés és például ilyen részletet nem fogad el: February 942, 1996. A gyakorlatban emiatt érdemes a 2 változatot használni. Dátumok kerekítése Azt érdemes mindig szem előtt tartani, hogy a Java Date class egy long
típusban reprezentálja a dátumot, mint időpontot. Emiatt a Date nem is tudhatja magáról, hogy ő milyen időzónában mutat egy konkrét értéket, de nem is erre lett kitalálva. A feladata csupán annyi, hogy egy konkrét értéket eltároljon Ennyi megjegyzés után nézzük meg a ceiling() metódust, ami úgy működik, hogy egy dátumra megadjuk azt a részt amitől „ jobbra” eső értékeket kinullázunk és a megadott részt inkrementáljuk. Nevezhetnénk ezt a dátumok felfelé kerekítésének is, ahol a 2. paraméter a kerekítési pontot jelöli ki. Nézzünk rá példákat! S t r i n g [ ] p a t t e r n = new S t r i n g [ ] { " yyyy−MM−dd ␣HH:mm: s s å " }; Date pd = D a t e U t i l s . p a r s e D a t e S t r i c t l y ( " 2015−03−01 ␣å 08:07:02 " , pattern ) ; // Eredmény : Sun Mar 01 0 8 : 0 7 : 0 2 GMT 2015 System . o u t p r i n t l n ( pd ); pd = D a t e U t i l s . c e i l i n g ( pd , C a l e n d a r HOUR) ; //
Eredmény : Sun Mar 01 0 9 : 0 0 : 0 0 GMT 2015 System . o u t p r i n t l n ( pd ); Date pd = D a t e U t i l s . p a r s e D a t e S t r i c t l y ( " 2015−03−01 ␣å 08:29:02 " , pattern ) ; // Eredmény : Sun Mar 01 0 8 : 2 9 : 0 2 GMT 2015 System . o u t p r i n t l n ( pd ); pd = D a t e U t i l s . round ( pd , C a l e n d a r HOUR) ; // Eredmény : Sun Mar 01 0 8 : 0 0 : 0 0 GMT 2015 System . o u t p r i n t l n ( pd ); pd = D a t e U t i l s . p a r s e D a t e S t r i c t l y ( " 2015−03−01 ␣ 0 8 : 3 0 : 0 2 "å , pattern ) ; // Eredmény : Sun Mar 01 0 8 : 3 0 : 0 2 GMT 2015 System . o u t p r i n t l n ( pd ); pd = D a t e U t i l s . round ( pd , C a l e n d a r HOUR) ; // Eredmény : Sun Mar 01 0 9 : 0 0 : 0 0 GMT 2015 System . o u t p r i n t l n ( pd ); pd = D a t e U t i l s . p a r s e D a t e S t r i c t l y ( " 2015−03−16 ␣ 0 8 : 3 0 : 0 2 "å , pattern ) ; pd = D a t e U t i l s . round ( pd , C a l e n d a r
MONTH) ; // Eredmény : Sun Mar 01 0 0 : 0 0 : 0 0 GMT 2015 System . o u t p r i n t l n ( pd ); pd = D a t e U t i l s . p a r s e D a t e S t r i c t l y ( " 2015−03−17 ␣ 0 8 : 3 0 : 0 2 "å , pattern ) ; pd = D a t e U t i l s . round ( pd , C a l e n d a r MONTH) ; // Eredmény : Wed Apr 01 0 0 : 0 0 : 0 0 GMT 2015 System . o u t p r i n t l n ( pd ); A fenti példákban látható, hogy mikor kerekítünk lefelé, illetve felfelé, amikor a releváns mező az óra vagy a hónap. A truncate() gondolkodás nélkül kinulláz minden olyan dátum mezőt, ami a megadott komponens után van: S t r i n g [ ] p a t t e r n = new S t r i n g [ ] { " yyyy−MM−dd ␣HH:mm: s s å " }; Date pd = D a t e U t i l s . p a r s e D a t e S t r i c t l y ( " 2015−03−01 ␣å 08:07:02 " , pattern ) ; // Eredmény : Sun Mar 01 0 8 : 0 7 : 0 2 GMT 2015 System . o u t p r i n t l n ( pd ); pd = D a t e U t i l s . t r u n c a t e ( pd , C a l e n d a r
HOUR) ; // Eredmény : Sun Mar 01 0 8 : 0 0 : 0 0 GMT 2015 System . o u t p r i n t l n ( pd ); pd = D a t e U t i l s . p a r s e D a t e S t r i c t l y ( " 2015−03−16 ␣ 0 8 : 3 0 : 0 2 "å , pattern ) ; pd = D a t e U t i l s . t r u n c a t e ( pd , C a l e n d a r MONTH) ; // Eredmény : Sun Mar 01 0 0 : 0 0 : 0 0 GMT 2015 System . o u t p r i n t l n ( pd ); pd = D a t e U t i l s . c e i l i n g ( pd , C a l e n d a r DAY OF MONTH) ; // Eredmény : Mon Mar 02 0 0 : 0 0 : 0 0 GMT 2015 System . o u t p r i n t l n ( pd ); Ennek is létezik Calendar objektumon működő változata. pd = D a t e U t i l s . c e i l i n g ( pd , C a l e n d a r MONTH) ; // Eredmény : Wed Apr 01 0 0 : 0 0 : 0 0 GMT 2015 System . o u t p r i n t l n ( pd ); Az eddig eltelt idő Amikor például a DAY OF MONTH értéket adjuk meg, akkor ez a nap pozícióján való felfelé kerekítést jelentett, ahogy láthattuk is. A ceiling() függvénynek van olyan változata is, ami
Calendar objektumon működik. A round() metódus számára szintén a releváns dátum komponenst (hó, nap, óra, perc, ) kell megadni, de ez már a kerekítés logikája szerint fog működni, ahogy a következő példák mutatják: A fragment metódus család segítségével le tudjuk kérdezni az év, hónap, . eddig eltelt idejét különféle időegységekben (nap, óra, perc, .) A következő példák tanulmányozásával mindez gyorsan világos lesz: S t r i n g [ ] p a t t e r n = new S t r i n g [ ] " }; long f = D a t e U t i l s . g e t F r a g m e n t I n D a y s ( c , ); 62 { " yyyy−MM−dd ␣HH:mm: s s å TimeZone t z = TimeZone . getTimeZone ( " Europe / Budapest " ) ; Calendar c = GregorianCalendar . g e t I n s t a n c e ( tz ) ; SimpleDateFormat d t = new SimpleDateFormat ( " yyyy−MM−ddå ’T ’HH:mm: s s z " ) ; dt . setTimeZone ( t z ) ; // Eredmény : 2013−06−01T11 : 3 3 : 4 7CEST System . o u t p r i n t l n ( d
t f o r m a t ( c getTime ( ) ) ) ; C a l e n d a r .MONTHå Java programozói könyvtár // Eredmény : 1 System . o u t p r i n t l n ( f ); f = D a t e U t i l s . getFragmentInDays ( c , DAY OF MONTH) ; // Eredmény : 0 System . o u t p r i n t l n ( f ) ; f = D a t e U t i l s . getFragmentInHours ( c , DAY OF MONTH) ; // Eredmény : 11 System . o u t p r i n t l n ( f ) ; f = D a t e U t i l s . getFragmentInDays ( c , // Eredmény : 152 System . o u t p r i n t l n ( f ) ; // Eredmény : 21 System . o u t p r i n t l n ( Apache Commons - Lang Calendar . å A használatára egy rövid példa: Calendar . å C a l e n d a r .YEAR) ; f / 7 ); f = D a t e U t i l s . getFragmentInHours ( c , // Eredmény : 3659 System . o u t p r i n t l n ( f ) ; public s t a t i c boolean i s S a m e I n s t a n t ( C a l e n d a r c a l 1 , å Calendar c a l 2 ) ; public s t a t i c boolean i s S a m e I n s t a n t ( Date d a t e 1 , Date å date2 ) ; C a l e n d a r
.YEAR) ; Látva a pillanatnyi időpontot, értelmezzük gyorsan az egyes eredményeket! Az 1 érték azért jött ki, mert az ekvivalens a c.get(CalendarDAY OF MONTH)) lekérdezéssel, itt pedig éppen elseje van A 0 azt jelenti, hogy a hónapban még nem telt el egyetlen egész nap sem. A 11 arra utal, hogy 11:33 perckor már 11 óra eltelt a napból. A 153 az év eddig eltelt napjai, amit 7-tel osztva az eddig elmúlt heteket adja, ami 21. Ez igaz is, mert most éppen a 22 hétben járunk. Végül a 3659 az év eddig eltelt napjait jelenti. Röviden a többi metódust érdemes áttekinteni, de megjegyezzük, hogy ezeknek a Date típusra épülő változatai is rendelkezésre állnak: public s t a t i c long g e t F r a g m e n t I n M i n u t e s ( C a l e n d a r å calendar , int fragment ) ; public s t a t i c long g e t F r a g m e n t I n S e c o n d s ( C a l e n d a r å calendar , int fragment ) ; public s t a t i c long g e t F r a g m e n t I n M i l l i s e c o n d s ( C
a l e n d a r å calendar , int fragment ) ; Date pd = D a t e U t i l s . p a r s e D a t e S t r i c t l y ( " 2015−03−01 ␣å 08:29:02 " , pattern ) ; Date pd2 = D a t e U t i l s . p a r s e D a t e S t r i c t l y ( " 2015−03−01 ␣å 08:29:01 " , pattern ) ; // Eredmény : f a l s e , mert a secundum e l t é r System . o u t p r i n t l n ( D a t e U t i l s i s S a m e I n s t a n t ( pd , pd2 ) ) ; Az isSameLocalTime() csak a Calendar objektum esetén értelmes, ugyanis az eltárolja ezt az információt is: public s t a t i c boolean i s S a m e L o c a l T i m e ( C a l e n d a r c a l 1 , å Calendar c a l 2 ) ; Date Calendar konverzió Egy Calendar objektum getTime() metódusával kapunk egy Date objektumot. Ennek a fordítottja így lehetséges: public s t a t i c C a l e n d a r t o C a l e n d a r ( Date d a t e ) ; A formázás támogatása A DateFormatUtils class számos statikus format() metódus változattal támogatja a
formázott dátum vagy Calendar kiírást. Példa: TimeZone t z = TimeZone . getTimeZone ( " Europe / Budapest " ) ; Calendar c = GregorianCalendar . g e t I n s t a n c e ( tz ) ; D a t e F o r m a t U t i l s . f o r m a t ( c , " yyyy−MM−dd ’T ’HH:mm: s s z " ) ; Stopper óra A StopWatch class segítségével egy stopper órát tudunk használni. Logikai lekérdezések Az isSameDay() visszaadja, hogy egy dátum Tömbműveletek - ArrayUtils ugyanarra a napra esik-e. Ez azt jelenti gyaElemek hozzáadása egy tömbhöz korlatilag, hogy a dátum időrészétől eltekintve vizsgálja az egyezőséget. Az add() és addAll() metódusok képesek elemet public s t a t i c boolean isSameDay ( C a l e n d a r c a l 1 , å vagy elemeket betenni egy tömbbe, amit eredCalendar c a l 2 ) ; public s t a t i c boolean isSameDay ( Date d a t e 1 , Date d a t e 2 å ményül visszaadnak. Az int típusra például így ); néz ki a deklaráció: Sokszor arra vagyunk
kíváncsiak, hogy 2 idő- public s t a t i c i n t [ ] add ( i n t [ ] a r r a y , i n t e l e m e n t ) ; public s t a t i c i n t [ ] add ( i n t [ ] a r r a y , i n t i n d e x , i n t å pont teljesen egyezik-e, amire a isSameInstant() element ) ; public s t a t i c i n t [ ] a d d A l l ( i n t [ ] a r r a y 1 , i n t . a r r a y 2 å lekérdezés ad segítséget: ); 63 Java programozói könyvtár Apache Commons - Lang A használatra egy rövid példa, ami a tömb utolsó elemeként a 88-at is beszúrja: i n t [ ] a i = new i n t [ ] { 1 2 , 4 , 5 } ; a i = A r r a y U t i l s . add ( a i , 8 8 ) ; a i = ArrayUtils . addAll ( ai , 88 , 33 , 44) ; Erre is nézzünk 2 példát: String [ ] String [ ] array = A r r a y U t i l s . toArray ( "1" , "2" ) ; emptyArray = A r r a y U t i l s .< S t r i n g >t o A r r a y ( ) ; A toObject() szintén egy metódus család, az Az index arra szolgál, hogy megadjuk a beismert összes alaptípusra
működik. A feladata szúrás pozícióját. az, hogy egy skalár tömb alapján egy azzal ekvivalensnek tekinthető „becsomagoló osztályos” Elemek törlése tömböt adjon vissza. A remove() függvények képesek elemeket törölni egy tömbből. Ebből is annyi változat van, ahány alaptípus, nézzünk megint egy int-re: A r r a y U t i l s . remove (new i n t [ ] { 2 , public s t a t i c Double [ ] t o O b j e c t ( double [ ] a r r a y ) ; public s t a t i c I n t e g e r [ ] t o O b j e c t ( i n t [ ] a r r a y ) ; . 6 , 3} , 1) ; A példában az 1. pozíciójú elem kerül majd törlésre, azaz az eredmény tömb [2, 3] lesz. A Tartalmazás vizsgálat removeElement() a 2. paraméterben megadott A contains() család egy tömbben keres egy értéértéket törli, esetünkben a 6-ot. ket, siker esetén true a visszaadott érték: i n t [ ] a i = A r r a y U t i l s . removeElement (new i n t [ ] { 2 , 6 , å 3} , 6) ; A removeAll() az összes tömbelemet törli.
public s t a t i c boolean c o n t a i n s ( i n t [ ] a r r a y , i n t å valueToFind ) ; public s t a t i c boolean c o n t a i n s ( char [ ] a r r a y , char å valueToFind ) ; . Egy tömb klónozása A clone() függvény lehetővé teszi, hogy egy tömbnek elkészítsük a másolatát. A paraméter Keresés egy valamilyen típusú tömb, az eredmény pedig ennek a klónja. Az alábbiakban a float esetét Az indexOf() és lastIndexOf() metódusok egy érmutatjuk: téket keresnek a tömbben, találat esetén visszapublic s t a t i c f l o a t [ ] c l o n e ( f l o a t [ ] a r r a y ) ; adják annak az indexét, ellenkező esetben -1 a visszatérés. Minden típusra létezik egy változat, a példa kedvéért nézzük ismét az int-et: Konstrukciós műveletek A toMap() feladata, hogy egy alkalmas tömbből Java Map objektumot adjon vissza: public s t a t i c Map<O b j e c t , O b j e c t > toMap ( O b j e c t [ ] ; array )å Ezt teszi a következő szemléltető példa is:
Map colorMap = M a p U t i l s . toMap (new S t r i n g [ ] [ ] { "RED" , "#FF0000 " } , { "GREEN" , "#00FF00" } , { "BLUE" , "#0000FF" } } ) ; {{ A toArray() egy típus biztos tömböt ad vissza, amit a paramétereiből állít össze: public s t a t i c <T> T [ ] 64 t o A r r a y (T . items ) ; public s t a t i c i n t i n d e x O f ( i n t [ ] a r r a y , i n t ); public s t a t i c i n t i n d e x O f ( i n t [ ] a r r a y , i n t , int s t a r t I n d e x ) ; public s t a t i c i n t l a s t I n d e x O f ( i n t [ ] a r r a y , valueToFind ) ; public s t a t i c i n t l a s t I n d e x O f ( i n t [ ] a r r a y , valueToFind , i n t s t a r t I n d e x ) ; valueToFindå valueToFind å int å int å A lebegőpontos esetek egy további lehetőséggel is rendelkeznek, megadhatunk egy deltát: public s t a t i c i n t valueToFind , public s t a t i c i n t valueToFind , i n d e x O f ( double [ ] a r r a y ,
double å double t o l e r a n c e ) ; i n d e x O f ( double [ ] a r r a y , double å i n t s t a r t I n d e x , double t o l e r a n c e ) ; Java programozói könyvtár Apache Commons - Lang A {} jelek is részei a generált Stringnek. A másik hasznos metódus a reverse(), ami egy forAz isEmpty() és isNotEmpty() vizsgálja, hogy dított sorrendbe állított tömböt ad vissza. a tömb üres vagy nem üres. Ez minden alaptípusra működik természetesen: Logikai lekérdezések public s t a t i c boolean isEmpty ( double [ ] a r r a y ) ; public s t a t i c boolean isNotEmpty ( i n t [ ] a r r a y ) ; public s t a t i c <T> boolean isNotEmpty (T [ ] a r r a y ) ; . Osztályok - ClassUtils A ClassUtils osztály egy olyan könyvtári ruAz isEquals() 2 – akár több dimenziós – tömtingyűjtemény, ami kiegészíti a BeanUtils csoböt hasonlít össze és egyenlőségük esetén igazat mag lehetőségeit és könnyebben elérhetővé teszi ad vissza: a Java
reflection (önelemzés) funkciókat. A köpublic s t a t i c boolean i s E q u a l s ( O b j e c t a r r a y 1 , O b j e c t å array2 ) ; vetkező főbb függvényei vannak: Az isSameLength() 2 tömb paramétert kap és igaz a visszatérés, ha azok egyforma méretűek. • Nevek megszerzése. Résztömb visszaadása • Logikai lekérdezések A subarray() egy olyan tömböt ad vissza, ami az Az alábbi néhány példa a nevek megszerzését input tömb egy része: mutatja be: public s t a t i c long [ ] s u b a r r a y ( long [ ] a r r a y , . public s t a t i c <T> T [ ] int s t a r t I n d e x I n c l u s i v e , int endIndexExclusive ) ; s u b a r r a y (T [ ] a r r a y , int s t a r t I n d e x I n c l u s i v e å , int endIndexExclusive ) ; Skalár tömb lekérése o S t r = C l a s s U t i l s . getPackageCanonicalName ( c ) ; o S t r = C l a s s U t i l s . getPackageName ( c ) ; oStr = C l a s s U t i l s . getShortCanonicalName ( c ) ; Természetesen a
Class<T> megszerzése is lehetséges: Class c = C l a s s U t i l s . getClass ( " org cs b e a n u t i l s å Gyumolcs " ) ; Időnként jól jön, ha egy Integer, Double, . A metódusok kezelését egy egyszerű példán tömb skalár megfelelőjéhez gyorsan hozzá tudunk jutni, ezt valósítják meg a toPrimitive() keresztül érdemes áttekinteni, de előtte nézzük meg a szignatúráját: függvény különböző esetei: public s t a t i c double [ ] t o P r i m i t i v e ( Double [ ] public s t a t i c double [ ] t o P r i m i t i v e ( Double [ ] double v a l u e F o r N u l l ) ; . array ) ; array , å A példa pedig a következő: Egyéb hasznos lehetőségek Egy tömb elemeinek String reprezentációját adja a toString(), aminek használatára egy példa: int [ ] a i = A r r a y U t i l s . removeElement (new i n t [ ] { 2 , 3} , 6) ; System . o u t p r i n t l n ( A r r a y U t i l s t o S t r i n g ( a i ) ) ; // Eredmény : {2 ,3} public s t a
t i c Method g e t P u b l i c M e t h o d ( C l a s s <?> c l s , S t r i n g methodName , Class <? >. parameterTypes ) throws S e c u r i t y E x c e p t i o n , NoSuchMethodException 6, å Set s e t = C o l l e c t i o n s . unmodifiableSet ( ) ; Method method = C l a s s U t i l s . g e t P u b l i c M e t h o d ( s e t å g e t C l a s s ( ) , " isEmpty " , new C l a s s [ 0 ] ) ; O b j e c t r e s u l t = method . i n v o k e ( s e t , new O b j e c t [ ] ) ; Amikor Java önelemzésre is szükség van, érdemes mindig rápillantani a ClassUtils osztály további lehetőségeire is. 65 Java programozói könyvtár Apache Commons - Lang Szövegekkel kapcsolatos műveletek CharUtils . isAsciiAlpha ( ’ 3 ’ ) = false C h a r U t i l s . u n i c o d e E s c a p e d ( ’ ␣ ’ ) = u0020 StrSubstitutor Az StrSubstitutor class egy template, amibe név StringEscapeUtils szerinti feloldás hivatkozási pontokat tehetünk. Ezzel az osztállyal egy
Java, Java Script, HTML Nézzünk egy példát! és XML menekülő karaktersorozatokra lehet egy String resStr = StrSubstitutor . r e p l a c e S y s t e m P r o p e r t i e s ( " j a v a ␣=␣ $ { j a v a å Stringet alakítani. v e r s i o n } ␣ é s ␣OS␣=␣ $ { o s . name } " ) ; // Eredmény : j a v a = 1 . 6 0 26 é s OS = Linux System . o u t p r i n t l n ( r e s S t r ) ; A cserélendő név, érték párokat egy Map objektumba is tehetjük: Map valuesMap = HashMap ( ) ; valuesMap . put ( " a n i m a l " , " q u i c k ␣ brown ␣ f o x " ) ; valuesMap . put ( " t a r g e t " , " l a z y ␣ dog " ) ; S t r i n g t e m p l a t e S t r i n g = "The␣ $ { a n i m a l } ␣ jumped ␣ o v e r ␣ t h e å ␣${ t a r g e t } . " ; S t r S u b s t i t u t o r sub = new S t r S u b s t i t u t o r ( valuesMap ) ; S t r i n g r e s o l v e d S t r i n g = sub . r e p l a c e ( t e m p l a t e S t r i n g )
; // Eredmény : The q u i c k brown f o x jumped over t h e dog . public s t a t i c f i n a l S t r i n g e s c a p e E c m a S c r i p t ( S t r i n g å input ) ; Input : He d i d n ’ t ␣ say , ␣ " Stop ! " Output : ␣He␣ d i d n ’ t ␣ say , ␣ " Stop ! " p u b l i c ␣ s t a t i c ␣ f i n a l ␣ S t r i n g ␣ escapeHtml4 ( S t r i n g ␣ input ) ; I n p u t : ␣ " b r e a d " ␣&␣ " b u t t e r " Output : ␣&q u o t ; b r e a d&q u o t ; ␣& ; ␣&q u o t ; b u t t e r&q u o t ; p u b l i c ␣ s t a t i c ␣ f i n a l ␣ S t r i n g ␣ unescapeHtml4 ( S t r i n g ␣ i n p u t )å ; I n p u t : ␣&q u o t ; b r e a d&q u o t ; ␣& ; ␣&q u o t ; b u t t e r&q u o t ; Output : ␣ " b r e a d " ␣&␣ " b u t t e r " p u b l i c ␣ s t a t i c ␣ f i n a l ␣ S t r i n g ␣ escapeXml ( S t r i n g ␣ i n p u t ) ; p u b l i c
␣ s t a t i c ␣ f i n a l ␣ S t r i n g ␣ unescapeXml ( S t r i n g ␣ i n p u t ) ; lazy å Az osztály számos metódussal támogatja a Egyéb lehetőségek Stringek minták alapján való megkonstruálását, így amikor ilyen feladataink vannak, érdemes BooleanUtils megfontolni a használatát. A Boolean standard Java osztály könnyebb kezelését támogatja. Példa: StrTokenizer B o o l e a n U t i l s . o r ( B o o l e a n TRUE, B o o l e a n TRUE) å Amikor a Java beépített StringTokenizer osztályt használjuk, de valamilyen funkció nincs meg vagy túl nehézkes, akkor javasolt ennek az osztálynak az átnézése, lehetséges, hogy pont itt lesz a hiányolt funkció. = B o o l e a n .TRUE B o o l e a n U t i l s . o r ( B o o l e a n FALSE , B o o l e a n FALSE) å = B o o l e a n . FALSE B o o l e a n U t i l s . o r ( B o o l e a n TRUE, B o o l e a n FALSE) å = B o o l e a n .TRUE B o o l e a n U t i l s . o r ( B o o l e a n TRUE, B o o l e a n
TRUE, B o o l e a n å TRUE) = B o o l e a n .TRUE B o o l e a n U t i l s . o r ( B o o l e a n FALSE , B o o l e a n FALSE , B o o l e a n å TRUE) = B o o l e a n .TRUE B o o l e a n U t i l s . o r ( B o o l e a n TRUE, B o o l e a n FALSE , B o o l e a n å TRUE) = B o o l e a n .TRUE B o o l e a n U t i l s . o r ( B o o l e a n FALSE , B o o l e a n FALSE , B o o l e a n å FALSE) = B o o l e a n . FALSE CharUtils Az osztály a karakterekkel kapcsolatos legfontosabb műveletek és lekérdezések gyűjtőhelye. Nem soroljuk fel a összes lehetőséget, de néhány példán keresztül ízelítőt szeretnénk adni a lehetőségekről: CharUtils CharUtils CharUtils CharUtils 66 . . . . isAscii ( ’3 ’ ) i s A s c i i ( ’− ’ ) i s A s c i i ( ’ ’ ) isAsciiAlpha ( = true = true = true ’ a ’ ) = true RandomStringUtils Véletlen karaktersorozatok generálását teszi lehetővé. A random() metódusa több lehetséges módon használható. Az Apache
Commons Lang még tartalmaz számos, ritkábban használt osztályt, azoknak a felderítését az olvasóra bízzuk. Java programozói könyvtár 5. Joda.org - Programozás dátumokkal és időkkel Joda.org - Programozás dátumokkal és időkkel Az egyik leggyakoribb típus a dátum és az idő. Az idő kezelésének igénye szinte minden alkalmazásban felmerül, így ennek hatékony, sokrétegű és kényelmes használata elengedhetetlen A Java ezen a téren nem ad kielégítő megoldást, a Calendar osztály is éppen csak megfelelő. A problémát felismerve született a Joda Time könyvtár (webhely: http://joda-time.sourceforgenet/) A Java 8 kiadásban tervezik a dátum és időkezelés funkciókat erőteljesebbé tenni, amihez majd ezt a könyvtárat is felhasználhatják, így érdemes már most megtanulni. dátum reprezentációjával, alapvetően ez biztosítja az oda-vissza integrációt is. A Joda-Time A 2002 óta fejlesztett Joda-Time egy kiváló miezt a fogalmat
a ReadableInstant interface-en kenőségű dátum és időtípus implementáció, amit resztül biztosítja, aminek a következő implemena Java beépített Calendar vagy Date osztálya tációi léteznek: helyett érdemes használni, mert azoknál sokkal • Instant: Egy egyszerű, csak olvasható többet tud, rugalmasabb és könnyebben haszimplementáció, ami csak az UTC-t tudja. nálható. A következő naptári rendszereket isAkkor érdemes használni, ha időzóna és meri: naptár független adatkezelésre van szük• Gregorián ség. Miért használjuk a Joda-Time-ot? • Júlián • Buddhista • Etióp • Kopt keresztény • Iszlám A könyvtár képes a beépített Java típusokkal együttműködni, hiszen sok más API azokat igényli. Alapfogalmak • DateTime: Ez a leggyakrabban használt típus, mindent tud és ez is csak olvasható, azaz úgy kell kezelni, mint például a String típust. • DateMidnight: A DateTime változata, ahol az idő komponens mindig
nulla, azaz egy nap pontos kezdetét reprezentálja. • MutableDateTime: A DateTime változtatható esete, olyan, mint a String esetén a StringBuffer. Részleges dátumok (Partials) A ReadablePartial interface jelenti ezt a fogalAz időpont a legalapvetőbb fogalom, a folya- mat, aminek a jellegzetessége az, hogy valamimatosan előrehaladó idő 1 pontja, amit mil- lyen időkomponenst nem tartalmaz. Az elérhető liszekundumban mérünk. Az origó az 1970- implementációk a következőek: • LocalDate: Az időzóna nincs eltárolva, az 01-01T00:00Z. Ez azt jelenti, hogy például az 1963.0105 értéke a következő negatív szám: adatszerkezet elsősorban az év, hó és nap 220579200000. Fontos kiemelni, hogy ez az ezadatokra fókuszál, erre biztosít számos metódust redmásodperc reprezentáció kompatibilis a Java Időpontok (Instants) 67 Java programozói könyvtár Joda.org - Programozás dátumokkal és időkkel • LocalTime: Az időzóna nincs eltárolva, az I
n s t a n t + D u r a t i o n = I n s t a n t adatszerkezet elsősorban az idő adatokra fókuszál (a dátumra nem), erre biztosít Periódusok (Periods) számos metódust. • LocalDateTime: Az időzóna nincs eltá- A periódus valamely időközönkénti újabb és újabb időpont előállítását jelenti. Itt nem egyrolva, de a dátum és idő részek igen szerűen arról van szó, hogy egy fix értéket adok • YearMonth: Ez is csak olvasható class és az előzőhöz, mert a periódusok ezredmásodpercegy év, hó párost reprezentál. ben vett távolsága eltérő lehet. Itt gondoljunk csak arra, hogy vedd a következő hóna• MonthDay: Csak olvasható osztály és egy pot. Ennek távolsága eltér januárfebruár és hónap, nap adategyüttest (például szülefebruármárcius esetében. Az általános logikát tésnap) tud eltárolni. a következő séma adja meg: • Partial : Ez a class képes a dátum és I n s t a n t + P e r i o d = I n s t a n t idő adatrészek
bármely kombinációját elPeriódus persze az is lehet, hogy, hogy 4 év, tárolni. 3 hónap és 2 nap. A periódust a ReadablePeA dokumentáció az alábbi összefüggést adja meg riod interface-t implementáló osztályok valósítaz Instants és Partials között: ják meg, nézzük őket! P a r t i a l + Missing F i e l d s + Time Zone = I n s t a n t • Years: Valahány évente változó periódus. Intervallumok (Intervals) Amikor egy tól-ig időpontot szeretnénk eltárolni, akkor ReadableInterval interface-t implementáló 2 class egyike lehet a legalkalmasabb: • Interval : A dátum-idő intervallumok létrehozásához mindig meg kell adnunk a kezdő és záró dátumot, ezt sokféle módon biztosítja ez a csak olvasható osztály. Általában ezt használjuk. • MutableInterval : Ezen class az Interval osztályhoz hasonló, de írható objektum, azaz „helyben” szerkeszthetőek az adatai. • Months: Valahány havonta változó periódus. • Weeks: Valahány
hetente változó periódus. • Days: Valahány naponta változó periódus. • Hours: Valahány óránként változó periódus. • Minutes: Valahány percenként változó periódus. Időtartamok (Durations) • Seconds: Valahány másodpercenként változó periódus. A ReadableDuration interface-t megvalósító objektumok az ezredmásodpercben mért eltelt időt képes tárolni és kezelni. Az interface-t a Duration osztály implementálja Az egyik legtermészetesebb művelete az, amikor egy időponthoz adjuk hozzá: • Period : Ez egy csak olvasható és rugalmas megvalósítása a periódusnak. Ellentétben az eddigieknek, itt megadhatjuk az összes dátum-idő komponenst, azaz olyan periódust is megalkothatunk, aminek év, hó, nap, óra, . része is van 68 Java programozói könyvtár Joda.org - Programozás dátumokkal és időkkel • MutablePeriod : A működése hasonló a PeA Joda-Time az időzónát a DateTimeZone riod osztályhoz, de változtatható az
ér- osztály segítségével tárolja és kezeli. Így tudunk téke. Ez akkor hasznos, ha nem mindig egy ilyen objektumot elkészíteni: z o n e = DateTimeZone . f o r I D ( " Europe / London "å ugyanakkora lépéssel akarunk haladni az DateTimeZone ); időben, mert alkalmas pillanatban megválAz UTC így adható meg: toztathatjuk az értékét. DateTimeZone zoneUTC = DateTimeZone .UTC; Naptár rendszerek (Chronology) További példák: DateTimeZone d e f a u l t Z o n e = DateTimeZone . g e t D e f a u l t ( ) ; DateTimeZone . s e t D e f a u l t ( myZone ) ; Már a Java is rendelkezik a GregorianCalendar osztállyal, de a Joda-Time Chronology class még ennél is több naptár rendszert képes kezelni. A Példák a Joda-Time használatára használata így lehetséges: Az alapfogalmak megértése után a Joda-Time DateTime d t = new DateTime ( C o p t i c C h r o n o l o g y . å getInstance () ) ; használatát úgy tanulhatjuk meg a leggyorsabEbben az esetben a
DateTime objektum a ban, ha a tipikus használatokra egy-egy kis pélmostani időpont Kopt keresztény kalendárium dát mutatunk be. Nézzük őket! szerinti időpontját fogja tartalmazni. A következő származtatott osztályok, azaz kronológiák A DateTime class vannak implementálva: A DateTime egy időpont tárolására alkalmas • ISOChronology osztály, aminek többféleképpen hozhatjuk létre az objektumait. Az alábbiakban bemutatjuk • GJChronology a leggyakoribb objektum készítési módszereket, • GregorianChronology mindegyiket egy rövid megjegyzéssel láttuk el. • JulianChronology • CopticChronology • BuddhistChronology • EthiopicChronology Időzónák (Time Zones) A Chronology class támogatja az időzónák használatát. A jelenlegi UTC rendszer szerinti idő a világidő, ehhez képest vannak besorolva az egyes (fő)városok, amihez ilyen azonosító String-eket használunk: America / M o n t r e a l America / Nassau America /New York . Europe / B r
a t i s l a v a Europe / B r u s s e l s Europe / Budapest Europe / Copenhagen Europe / G i b r a l t a r . package o r g . c s j o d a t i m e ; import import import import java . u t i l java . u t i l org . joda org . joda . Calendar ; . Date ; t i m e . DateTime ; t i m e . DateTimeZone ; public c l a s s DateTimeTest { public s t a t i c void t e s t 1 ( ) { // Rendszeridő a l a p j á n DateTime dateTime = new DateTime ( ) ; System . o u t p r i n t l n ( " dateTime ␣=␣ " + dateTime ) ; // Rendszeridő + időzóna DateTimeZone d t z = DateTimeZone . f o r I D ( " Europe /å Budapest " ) ; dateTime = dateTime . w i t h Z o n e ( d t z ) ; System . o u t p r i n t l n ( " dateTime ␣ 1 ␣=␣ " + dateTime ) ; // D i r e k t i d ő p o n t megadással // év , hó , nap , óra , perc , másodperc , m i l l i s e c dateTime = new DateTime ( 2 0 1 3 , 6 , 8 , 0 , 0 , 0 , 0 ) ; System . o u t p r i n t l n ( " dateTime ␣ 2 ␣=␣
" + dateTime ) ; // j a v a . u t i l Calendar a l a p j á n Calendar c a l e n d a r = Calendar . g e t I n s t a n c e ( ) ; dateTime = new DateTime ( c a l e n d a r ) ; System . o u t p r i n t l n ( " dateTime ␣ 3 ␣=␣ " + dateTime ) ; // j a v a . u t i l Date m i l l i s e c a l a p j á n Date d a t e = new Date ( ) ; dateTime = new DateTime ( d a t e . getTime ( ) ) ; System . o u t p r i n t l n ( " dateTime ␣ 4 ␣=␣ " + dateTime ) ; // j a v a . u t i l Date a l a p j á n dateTime = new DateTime ( d a t e ) ; System . o u t p r i n t l n ( " dateTime ␣ 5 ␣=␣ " + dateTime ) ; 69 Java programozói könyvtár Joda.org - Programozás dátumokkal és időkkel A következő hónap első napja } } // A l a p é r t e l m e z e t t formátum a l a p j á n dateTime = new DateTime ( " 2016−02−03T14å :15:00.000+08:00 ") ; System . o u t p r i n t l n ( " dateTime ␣ 6 ␣=␣ " + dateTime )
; dateTime = new DateTime ( " 2016−02−03 " ) ; System . o u t p r i n t l n ( " dateTime ␣ 7 ␣=␣ " + dateTime ) ; public s t a t i c void main ( S t r i n g [ ] { test1 () ; } args ) A fenti programot lefuttatva ez az eredmény jelenik meg a képernyőn: dateTime dateTime dateTime dateTime dateTime dateTime dateTime dateTime = 1 2 3 4 5 6 7 2013−06−08T09 : 0 7 : 3 4 . 9 1 5 Z = 2013−06−08T11 : 0 7 : 3 4 . 9 1 5 + 0 2 : 0 0 = 2013−06−08T00 : 0 0 : 0 0 . 0 0 0 Z = 2013−06−08T09 : 0 7 : 3 5 . 0 3 3 Z = 2013−06−08T09 : 0 7 : 3 5 . 1 2 8 Z = 2013−06−08T09 : 0 7 : 3 5 . 1 2 8 Z = 2016−02−03T06 : 1 5 : 0 0 . 0 0 0 Z = 2016−02−03T00 : 0 0 : 0 0 . 0 0 0 Z Használhatnánk a DateTime osztályt is, de most gyakorlásképpen a „helyben” változtatható, azaz írható MutableDateTime class segítségével nézzük meg, hogyan kérhetjük le például a következő hónap első napját. public c l a s s DateTimeTest { . public
s t a t i c void f i r s t D a y O f N e x t M o n t h ( ) { MutableDateTime dateTime = new MutableDateTime ( ) ; System . o u t p r i n t l n ( " dateTime ␣=␣ " + dateTime ) ; dateTime . addMonths ( 1 ) ; dateTime . setDayOfMonth ( 1 ) ; dateTime . s e t M i l l i s O f D a y ( 0 ) ; System . o u t p r i n t l n ( " dateTime ␣=␣ " + dateTime ) ; System . o u t p r i n t l n ( "A␣ h é t ␣ n a p j a ␣=␣ " + dateTime å getDayOfWeek ( ) ) ; Szeretnénk kiemelni a dateTime 1 sort, ahol } . egy DateTimeZone class példányt használva } olyan DateTime objektumot kértünk le, ami a A dateTime objektumba először a pillanatnyi budapesti időzóna szerint mutatja az időt. Látdátumot kérjük le, majd hozzáadunk 1 hónapot, hatjuk, hogy ez +2 óra az UTC-hez képest, azaz beállítjuk a napot 1-re. A szépség kedvéért az a 9 óra helyett 11 órát mutat. időt éjfélre állítottuk. Az eredmény a következő: Dátumok növelése,
csökkentése dateTime = 2013−06−08T19 : 3 9 : 0 0 . 5 3 1 Z dateTime = 2013−07−01T00 : 0 0 : 0 0 . 0 0 0 Z A hét napja = 1 Egy DateTime objektum minden olyan metóAzt is láthatjuk, hogy ez a nap a hét első dussal rendelkezik, ami az értékének értelmes megváltoztatását jelenti. Itt most nézzük meg napja, azaz hétfő a növelések és csökkentések lehetőségét! public c l a s s DateTimeTest { . public s t a t i c void t e s t 2 ( ) { DateTime dateTime = new DateTime ( ) ; System . o u t p r i n t l n ( " dateTime ␣=␣ " + dateTime ) ; Napok száma 2 dátum között Itt az idő, hogy kipróbáljuk a DateMidnight osztályt is, bár a többivel is el tudnánk végezni a dateTime = dateTime . p l u s H o u r s ( 3 ) ; System . o u t p r i n t l n ( " dateTime ␣=␣ " + dateTime ) ; feladatot. Ez viszont egyből készen adja az éjdateTime = dateTime plusWeeks ( 2 ) ; félre állítást, azaz 2 napkezdést tudunk csinálni, System . o
u t p r i n t l n ( " dateTime ␣=␣ " + dateTime ) ; } amit a start és end változók hivatkoznak meg. . } Azt is látjuk, hogy a Days periódus fajtájú oszA példában a dateTime objektumhoz előbb tályt olyan célra is használhatjuk, hogy segítsé3 órát, majd 2 hetet adtunk, aminek a futási gével lekérjük a 2 instant közötti napok számát. eredménye ez lett: public c l a s s DateTimeTest dateTime = 2013−06−08T11 : 3 4 : 3 9 . 2 5 9 Z dateTime = 2013−06−08T14 : 3 4 : 3 9 . 2 5 9 Z dateTime = 2013−06−22T14 : 3 4 : 3 9 . 2 5 9 Z Egy DateTime objektumbak minden komponense előjelesen növelhető, az éveket például a plusYears() metódussal változtathatjuk. 70 { . public s t a t i c void daysBetween2Days ( ) { D a t e M i d n i g h t s t a r t = new D a t e M i d n i g h t ( " 1963−01−05 "å ); D a t e M i d n i g h t end = new D a t e M i d n i g h t ( " 2013−06−08 " ) ; i n t d a y s = Days . daysBetween ( s t a
r t , end ) g e t D a y s ( ) ; Java programozói könyvtár } . } System . o u t p r i n t l n ( " Days ␣ between ␣ " + s t a r t å t o S t r i n g ( " yyyy−MM−dd " ) + " ␣ and ␣ " + end . t o S t r i n g ( " yyyy−MM−dd" ) + " ␣=␣ "å + days + " ␣ day ( s ) " ) ; A futási eredmény ez lett: Joda.org - Programozás dátumokkal és időkkel Formázott kiírás A formázás az ismert Java szabályok szerint történik itt nincs semmi újdonság. A DateTime esetén például így tudunk formázni a toString() metóduson keresztül: Days between 1963−01−05 and 2013−06−08 = 1 8 4 1 7 day ( s ) private s t a t i c f i n a l S t r i n g p a t t e r n = "E␣MM/dd/ yyyy ␣HH:mm: s s . SSS" ; DateTime dateTime = new DateTime ( ) ; System . out p r i n t l n ( dateTime t o S t r i n g ( p a t t e r n ) ) ; System . out p r i n t l n ( dateTime t o S t r i n g ( p a t t e r n ,
L o c a l e GERMANY) ) ; System . out p r i n t l n ( dateTime t o S t r i n g ( p a t t e r n , L o c a l e FRENCH) ) ; System . out p r i n t l n ( dateTime t o S t r i n g ( p a t t e r n , L o c a l e JAPANESE) ) ; Hónapok száma 2 dátum között Gyakorlásképpen nézzük meg a 2 időpont között eltelt hónapok számát is, amihez persze most a Months class lesz használva. public c l a s s DateTimeTest { . public s t a t i c void monthsBetween2Days ( ) { DateMidnight s t a r t = new DateMidnight ( "1963−01−05" ) ; DateMidnight end = new DateMidnight (new Date ( ) ) ; i n t months = Months . monthsBetween ( s t a r t , end ) getMonths ( ) ; } . } System . out p r i n t l n ( "Months␣ between ␣ " + s t a r t t o S t r i n g ( " yyyy−MM−dd" ) + " ␣ and ␣ " + end . t o S t r i n g ( " yyyy−MM−dd" ) + " ␣=␣ " + months ) ; Az eredmény: Months between 1963−01−05 and 2013−06−08 =
605 A dátum egyes részeinek megszerzése A most következő példában azt fogjuk bemutatni, hogy egy DateTime objektum részeit mi- lyen módon kérhetjük le. Talán nem is kell kiemelnünk, de azért megjegyezzük, hogy természetesen a többi instant class (DateMidnight, ) esetén is pont ugyanígy tennénk. Nézzük a példát! public c l a s s DateTimeTest { . public s t a t i c void partOfDateTime ( ) { DateTime dateTime = new DateTime ( ) ; System . out p r i n t l n ( " dateTime ␣=␣ " + dateTime ) ; 71 Java programozói könyvtár Joda.org - Programozás dátumokkal és időkkel // Hányadik napon vagyunk System . out p r i n t l n ( "DOY␣=␣ " + dateTime getDayOfYear ( ) ) ; System . out p r i n t l n ( "DOM␣=␣ " + dateTime getDayOfMonth ( ) ) ; System . out p r i n t l n ( "DOW␣=␣ " + dateTime getDayOfWeek ( ) ) ; // Az év h e t é n e k száma System . out p r i n t l n ( "WOW␣=␣ " +
dateTime getWeekOfWeekyear ( ) ) ; // óra , perc , másodperc a napon b e l ü l System . out p r i n t l n ( "HOD␣=␣ " + dateTime getHourOfDay ( ) ) ; System . out p r i n t l n ( "MOH␣=␣ " + dateTime getMinuteOfHour ( ) ) ; System . out p r i n t l n ( "SOM␣=␣ " + dateTime getSecondOfMinute ( ) ) ; } . } // óra , perc , másodperc az éven b e l ü l System . out p r i n t l n ( "MOD␣=␣ " + dateTime getMinuteOfDay ( ) ) ; System . out p r i n t l n ( "SOD␣=␣ " + dateTime getSecondOfDay ( ) ) ; A példában bemutatott mezőkön (Date/Time fields) kívül még számos másik is létezik. Az ISO dátumformátumok Az ISODateTimeFormat class sok alapértelmezett formázót tartalmaz, érdemes megtanulni a használatukat. Vegyünk néhány esetet át! // Legyen e g y dátumunk DateTime dateTime = new DateTime ( ) ; // yyyyMMdd : 20120228 dateTime . t o S t r i n g ( ISODateTimeFormat b a s i c D a t e (
) ) ) ; // 20120228 T163810 .037+0800 dateTime . t o S t r i n g ( ISODateTimeFormat basicDateTime ( ) ) ) ; // 20120228 T163810+0800 dateTime . t o S t r i n g ( ISODateTimeFormat b a s i c D a t e T i m e N o M i l l i s ( ) ) ) ; // yyyyDDD : 2012059 dateTime . t o S t r i n g ( ISODateTimeFormat b a s i c O r d i n a l D a t e ( ) ) ) ; // 2012W092 dateTime . t o S t r i n g ( ISODateTimeFormat basicWeekDate ( ) ) ) ; // 2012W092T163810.037+0800 dateTime . t o S t r i n g ( ISODateTimeFormat basicWeekDateTime ( ) ) ) ; A basicWeekDate() és basicWeekDateTime() eredményét úgy kell értelmezni, hogy ekkor nem a megszokott év, hó, nap koordinátában gondolkodunk, hanem azt mondjuk meg, hogy melyik év, hányadik hetének, hányadik napja. A 2012W092 ezek szerint azt jelenti, hogy 2012 év, 9. hetének 2 napja, azaz kedd (mindig 1=hétfő). 72 Integráció a Java SDK-val Már említettük, hogy ez fontos, mert más API-k a Java beépített idő és dátum kezelését
használják, így a Joda-Time használata esetén lesznek olyan helyzetek, amikor Calendar vagy Date objektumokkal kell kommunikálni: DateTime dateTime = new DateTime ( ) ; C a l e n d a r c a l e n d a r = dateTime . t o C a l e n d a r ( L o c a l e å getDefault () ) ; Date d a t e = dateTime . t o D a t e ( ) ; Java programozói könyvtár Joda.org - Programozás dátumokkal és időkkel A Java SDKDateTime (és egyéb instant) annak annak különféle információit. A követirányt már korábban bemutattuk kező lépésben az intervallum végét 1 hónappal meghosszabbítjuk, így 6. hó helyett 7 lesz Befejezésül lekérünk egy Duration példányt, ami az Intervallumok használata intervallum elejének és végének a különbségéből Elsőként nézzük meg, hogy egy intervallumot adódik. Az utolsó sorban a duration-t napokmiként tudunk létrehozni egy start és end dá- ban írjuk ki, de számos más mértékegységben is tumból! A testInterval() metódus 2
dátumból megtehettük volna. legyárt egy interval nevű példányt, majd kiírja public c l a s s DateTimeTest { . public s t a t i c void t e s t I n t e r v a l ( ) { DateMidnight s t a r t = new DateMidnight ( "1963−01−05" ) ; DateMidnight end = new DateMidnight ( "2013−06−08" ) ; I n t e r v a l i n t e r v a l = new I n t e r v a l ( s t a r t , end ) ; // I n t e r v a l = 1963−01−05T00 : 0 0 : 0 0 . 0 0 0 Z/2013−06−08T00 : 0 0 : 0 0 0 0 0 Z System . out p r i n t l n ( " I n t e r v a l ␣=␣ " + i n t e r v a l ) ; // S t a r t = 1963−01−05T00 : 0 0 : 0 0 . 0 0 0 Z System . out p r i n t l n ( " S t a r t ␣ ␣ ␣ ␣=␣ " + i n t e r v a l g e t S t a r t ( ) ) ; // End = 2013−06−08T00 : 0 0 : 0 0 . 0 0 0 Z System . out p r i n t l n ( "End␣ ␣ ␣ ␣ ␣ ␣=␣ " + i n t e r v a l getEnd ( ) ) ; i n t e r v a l = i n t e r v a l . withEnd ( i n t e r v a l getEnd ( ) plusMonths ( 1
) ) ; // I n t e r v a l = 1963−01−05T00 : 0 0 : 0 0 . 0 0 0 Z/2013−07−08T00 : 0 0 : 0 0 0 0 0 Z System . out p r i n t l n ( " I n t e r v a l ␣=␣ " + i n t e r v a l ) ; } . } Duration duration = i n t e r v a l . toDuration ( ) ; // Duration = 18447 System . out p r i n t l n ( " D u r a t i o n ␣=␣ " + d u r a t i o n ) ; Az Instant class Időzónák használata Érdemes a ritkábban használt Instant class-ra is rápillantani. A folyamatosan telő idő zérus pontját már említettük, ez a 1970-01-01T00:00Z A következő instant objektum egy ennél 1 másodperccel (1000 ezredmásodperccel) későbbi időpontot tárol: Az üzleti alkalmazásokban a lokális idő kezelése nagyon fontos, hiszen amikor Amerikában alszanak, akkor Európában dolgoznak. Az egyezményes koordinált világidő vagy röviden koordinált világidő (angolul rövidítéssel: UTC ) az a hivatkozási időzóna, amelyhez a Föld többi időzónáI n s t a n t i n
s t a n t = new I n s t a n t ( 1 0 0 0 ) ; ját viszonyítjuk. Az UTC a greenwichi középEhhez adjunk még fél másodpercet, majd időt (GMT ) váltotta 1961-ben, de máig mindkét jelölést használják, noha a két fogalom nem azovonjunk ki negyedet: nos. Az UTC használata ajánlott, a GMT mint instant = instant . plus (500) ; i n s t a n t = i n s t a n t . minus ( 2 5 0 ) ; fogalom elavultnak számít. Az egyezményes ko73 Java programozói könyvtár ordinált világidő a nagy pontossággal, a világ 50 különböző laborjában egyenletesen mért nemzetközi atomidőből (International Atomic Time, TAI) származik. A Föld időzónáit az UTC-hez viszonyítva állapítják meg és mivel a greenwichi középidőt váltotta, az az időzóna maradt a viszonyítási pont. Az attól keletre eső időzónák 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 Joda.org - Programozás dátumokkal és időkkel pozitív, míg a
nyugatra találhatók negatív értékű órával térnek el (vannak nem egész órával eltérő időzónák is). A Joda-Time mindezt remekül kezeli, sőt ismeri az egyes országokra jellemző óra előre és hátraállítást is Szokásunkhoz híven mindezt az alábbi testTimeZone() metóduson keresztül szeretnénk elmagyarázni. public c l a s s DateTimeTest { . public s t a t i c void testTimeZone ( ) { DateTimeZone t z K i e v = DateTimeZone . f o r I D ( " Europe / Kiev " ) ; DateTimeZone tzBp = DateTimeZone . f o r I D ( " Europe / Budapest " ) ; DateTime utcTime = new DateTime ( 2 0 1 3 , 5 , 9 , 2 1 , 9 , DateTimeZone .UTC) ; // UTC:2013−05−09T21 : 0 9 : 0 0 . 0 0 0 Z System . out p r i n t l n ( "UTC: "+utcTime ) ; DateTime l o c a l T i m e = utcTime . withZone ( t z K i e v ) ; // Kiev :2013−05−10T00 : 0 9 : 0 0 . 0 0 0 + 0 3 : 0 0 System . out p r i n t l n ( " Kiev : " + l o c a l T i m e ) ; l o c a l T i m e = utcTime
. withZone ( tzBp ) ; // Budapest :2013−05−09T23 : 0 9 : 0 0 . 0 0 0 + 0 2 : 0 0 System . out p r i n t l n ( " Budapest : " + l o c a l T i m e ) ; l o c a l T i m e = new DateTime ( 2 0 1 2 , 1 1 , 2 9 , 1 3 , 4 0 , tzBp ) ; // Budapest ( november ) :2012−11−29T13 : 4 0 : 0 0 . 0 0 0 + 0 1 : 0 0 System . out p r i n t l n ( " Budapest ␣ ( november ) : " + l o c a l T i m e ) ; utcTime = l o c a l T i m e . withZone ( DateTimeZone UTC) ; // UTC ( november ) :2012−11−29T12 : 4 0 : 0 0 . 0 0 0 Z System . out p r i n t l n ( "UTC␣ ( november ) : "+utcTime ) ; } . } l o c a l T i m e = new DateTime ( 2 0 1 2 , 5 , 1 1 , 1 3 , 4 0 , tzBp ) ; // Budapest ( május ) :2012−05−11T13 : 4 0 : 0 0 . 0 0 0 + 0 2 : 0 0 System . out p r i n t l n ( " Budapest ␣ ( május ) : " + l o c a l T i m e ) ; utcTime = l o c a l T i m e . withZone ( DateTimeZone UTC) ; // UTC ( május ) :2012−05−11T11 : 4 0 : 0 0 . 0 0 0 Z System . out p
r i n t l n ( "UTC␣ ( május ) : "+utcTime ) ; A 6. és 7 sorban egy budapesti és egy kievi időzónát reprezentáló objektumot készítettünk A paraméterül átadott szöveg szabvány, ezt a Java SDK is tartalmazza. A 9 sorban egy 2013.0509 napi 21:09 időpontot rögzítettünk az utcTime változóhoz, UTC, azaz világidővel. A 11. sor megjegyzésében láthatjuk is, hogy a 12 sor kiíró utasítása mit jelenít meg erről. A 13 sor nagyon tanulságos, mert láthatjuk belőle, 74 hogy egy UTC időpontot miként alakíthatunk a kievi időre. A 17 sorban pedig a budapestire Láthatjuk a megjelenített értékekből (14. és 18 sorok), hogy ezen a napon Kiev már 2013.0510-i napra tért át (0 óra 9 perc), míg Budapest még aznap van, de 2 órával előbbre (23 óra 09 perc). A 21-33 sorok kódjában 2 érdekesség is rejtőzik, de mindkettő nagyon lényeges. Láthatjuk, hogy a helyi időponthoz milyen egyszerűen tud- Java programozói könyvtár juk
megszerezni az UTC időt. A másik érdekesség, hogy egyes országok (Magyarország is) az időzónán belül téli és nyári időszámítással is rendelkezik. Ez az oka annak, hogy októberben (2012.1129) a helyi és UTC idő között csak 1 óra különbséget látunk, míg májusban (2012.0511) 2 órát. A java.sqlDate integráció Joda.org - Programozás dátumokkal és időkkel vissza, ami ezen mező (azaz property) maximumát veszi fel. Esetünkben ez a field most a nap természetesen. Ezzel elérkeztünk a szilveszter időpontjához, amihez 1 napot adva az újévet kaptuk vissza. DateTime newYear = new DateTime ( DateTimeZone .UTC) å dayOfYear ( ) . withMaximumValue ( ) p l u s D a y s ( 1 ) ; Nézzük az új év első szerda napját: DateTime f i r s t W e d n e s d a y = newYear . p l u s D a y s ( å DateTimeConstants .THURSDAY − newYear getDayOfWeekå () ) ; A Java JDBC dátum típus esetén az SQL adatbázisokkal a java.sqlDate típuson keresztül tartja a
kapcsolatot. Szerencsére ezt is könnyen használni tudjuk, mert a lekért ezredmásodperc segítségével például így tudunk ilyen típust készíteni: A kód lekéri a kivonás jobb oldalán azt, hogy a newYear időponthoz (azaz az újév napja) azt az információt, hogy ez most a hét hányadik napja (a mi konkrét esetünkben most 3, azaz szerda lesz). A THURSDAY konstans értéke 4, ezért a plusDays(1) lesz meghívva, azaz 2014. év j a v a . s q l Date s t a r t D a t e = new j a v a s q l Date ( első csütörtök 2014.0102 napon lesz new D a t e M i d n i g h t ( 2 0 1 2 , 1 1 , 5 , DateTimeZone .UTC) å getMillis () ) ; A következő példánk legyen az, hogy meghaj a v a . s q l Date endDate = new j a v a s q l Date ( new D a t e M i d n i g h t ( 2 0 1 2 , 1 1 , 8 , DateTimeZone .UTC) å tározzuk a következő péntek napját, ahol a kiingetMillis () ) ; dulás napját paraméterül kapjuk (ez lesz a d ). Sokat segít az is, ha látjuk a kétféle Java dá- A
dayOfWeek() egy LocalDate.Property objektum típus közötti oda-vissza konvertálás módját tumot adja vissza, aminek a kérdéses mezőjét is: (azaz most a napot) 5-re, azaz péntekre állít// A mostani p i l l a n a t juk. Amennyiben az input d nap péntek előtti, j a v a . u t i l Date now = new j a v a u t i l Date ( ) ; úgy ezen a héten, különben jövő héten (plus// A l a k í t s u k á t SQL Date−r e : j a v a . s q l Date s q l D a t e = new j a v a s q l Date ( now getTimeå Weeks(1)) lesz a következő péntek. () ) ; // A l a k í t s u k v i s s z a Java Date−r e : j a v a . u t i l Date u t i l D a t e = new j a v a u t i l Date ( s q l D a t e å getTime ( ) ) ; Deklarált nap meghatározása Ismerős mindenkinek az a nyelvjárás, hogy legyen a következő újévkor, vagy jövő év első csütörtökön. Ilyenkor úgy adunk meg egy időpontot, hogy nem a pontos számszerű jellemzőit specifikáljuk, hanem csak körbeírjuk Nézzük meg
először ennek a 2 példának a Joda-Time-ra épülő megfogalmazását! Lekérjük a mostani pillanatnyi időt a világidő szerint. A dayOfYear() metódus egy DateTimeProperty osztálybeli objektummal tér vissza, aminek a withMaximumValue() metódusa olyan DateTime objektumot ad public c l a s s DateTimeTest { . public s t a t i c L o c a l D a t e c a l c N e x t F r i d a y ( L o c a l D a t e d ) { L o c a l D a t e f r i d a y = d . dayOfWeek ( ) s e t C o p y ( 5 ) ; i f (d . isBefore ( friday ) ) { return d . dayOfWeek ( ) s e t C o p y ( 5 ) ; } else { return d . plusWeeks ( 1 ) dayOfWeek ( ) s e t C o p y ( 5 ) ; } } . } Felhívjuk a figyelmet az isBefore() metódusra, ami akkor ad vissza igaza, ha a paraméterül kapott időpont előtt vagyok. Ehhez hasonló segítő metódus nagyon sok van, itt nem is érdemes tételesen felsorolni őket. Befejezésül egy klasszikus példát mutatunk be. A kérdés úgy szól, hogy egy megadott évben mikor lesz húsvét
vasárnapja Itt ragad75 Java programozói könyvtár Joda.org - Programozás dátumokkal és időkkel juk meg az alkalmat, hogy megemlítsük a Jollyday könyvtárat (webhely: http://jollyday. sourceforge.net/) Ez arra lett tervezve, hogy együttműködjön a Joda-Time-mal és azt felkészítse olyan további tudással, ami az ünnepnapok kezelését profi szinten ismeri. Országfüggően tudja az egyes ünnepet, most azonban csak a húsvét napjának lekérdezésére szeretnénk használni, ahogy a következő példa bemutatja: public c l a s s DateTimeTest { . public s t a t i c void t e s t H u s v e t ( i n t ev ) { LocalDate l c = null ; de . j o l l y d a y u t i l C a l e n d a r U t i l cu = new de j o l l y d a y å . u t i l CalendarUtil () ; l c = cu . g e t E a s t e r S u n d a y ( ev ) ; // Vasárnap : 2013−03−31 System . o u t p r i n t l n ( l c ) ; } . } A CalendarUtil cu példányára meghívott getEasterSunday() metódus kap egy évet és visszaadja
azt a Joda-Time LocalDate objektumot, ami a húsvét vasárnapot reprezentálja. S t r i n g e x p i r a t i o n D a y = e x p i r a t i o n D a t e . toDateTime (new å DateTime ( DateTimeZone .UTC) dayOfMonth ( ) å withMinimumValue ( ) ) . dayOfWeek ( ) g e t A s T e x t ( ) ; System . o u t p r i n t l n ( e x p i r a t i o n D a y ) ; Mit csinál ez a kis kód? A expirationDate változó toDateTime() metódusa visszaadja a december 1-et, amit szövegesen megjelenítünk: Monday. Persze ezt a dátumot könnyebben is kitalálhattuk volna, hiszen minden hónap elsejével kezdődik. Most adjunk meg egy születésnapot reprezentáló hónap, nap objektumot (június 25.): MonthDay b i r t h d a y = new MonthDay ( 6 , 25) ; Vajon milyen napra esik a következő évben? A választ a dayOfWeek szöveg adja meg, amit az eddigiek alapján remélhetőleg már mindenki megért. S t r i n g dayOfWeek = b i r t h d a y . toDateTime (new DateTime ( å DateTimeZone .UTC) p l u s Y e a r s
( 1 ) ) dayOfWeek ( ) å getAsText ( ) ; Az idő intervallumokról bővebben Részleges dátum és időpontok Amikor egy napi feljegyzések vagy meeting szerA helyi nap és időpont megadása az ismert mó- vező alkalmazást készítünk nagy segítségre jöhet don lehetséges, ahogy a test3() első 2 sora is mu- az intervallumok használatának az a módja, ami tatja. A következő sor a partialDate objektumot óra, perc-re van kihegyezve Az alábbiakban a mai napra felvettünk néhány időintervallumot: kiegészíti a partialTime résszel. public c l a s s DateTimeTest { . public s t a t i c void t e s t 3 ( ) { L o c a l D a t e p a r t i a l D a t e = new L o c a l D a t e ( 2 0 1 2 , 1 2 , 3 ) å ; LocalTime p a r t i a l T i m e = new LocalTime ( 1 2 , 5 0 ) ; DateTime dateTime = p a r t i a l D a t e . toDateTime ( å partialTime , DateTimeZone .UTC) ; } . } // 1 2 : 5 0 : 0 0 . 0 0 0 System . o u t p r i n t l n ( p a r t i a l T i m e // 2012−12−03T12 : 5 0 : 0 0
. 0 0 0Z System . o u t p r i n t l n ( dateTime ) ; ); Amikor csak az év, hónapot akarjuk reprezentálni, akkor erre a YearMonth class kiválóan alkalmas: YearMonth e x p i r a t i o n D a t e = new YearMonth ( 2 0 1 4 , 1 2 ) ; A használatára pedig ez egy releváns példa: 76 DateTime t o d a y = new DateTime ( DateTimeZone .UTC) ; Interval f i r s t M e e t i n g = new I n t e r v a l ( new LocalTime ( 9 , 0 ) . toDateTime ( t o d a y ) , new LocalTime ( 1 0 , 0 ) . toDateTime ( t o d a y ) ) ; Interval c o f f e e B r e a k = new I n t e r v a l ( new LocalTime ( 1 0 , 0 ) . toDateTime ( t o d a y ) , new LocalTime ( 1 0 , 3 0 ) . toDateTime ( t o d a y ) ) ; Interval s e c o n d M e e t i n g = new I n t e r v a l ( new LocalTime ( 1 0 , 3 0 ) . toDateTime ( t o d a y ) , new LocalTime ( 1 2 , 0 ) . toDateTime ( t o d a y ) ) ; Interval lunchTime = new I n t e r v a l ( new LocalTime ( 1 3 , 0 ) . toDateTime ( t o d a y ) , new LocalTime ( 1 4 , 0 ) . toDateTime (
t o d a y ) ) ; Interval t h i r d M e e t i n g = new I n t e r v a l ( new LocalTime ( 1 6 , 0 ) . toDateTime ( t o d a y ) , new LocalTime ( 1 8 , 0 ) . toDateTime ( t o d a y ) ) ; Interval s p o r t E v e n t = new I n t e r v a l ( new LocalTime ( 1 7 , 0 ) . toDateTime ( t o d a y ) , new LocalTime ( 1 9 , 0 ) . toDateTime ( t o d a y ) ) ; Az első művelet, amit bemutatunk az 2 intervallumunk közötti szünet (GAP), ami szintén egy intervallum. Esetünkben a 3 meeting és az Java programozói könyvtár ebéd közötti időt számítottuk ki és tettük bele a gap változóba: Joda.org - Programozás dátumokkal és időkkel Ezek hosszának összehasonlítása: int i = o1 . t o D u r a t i o n ( ) compareTo ( o2 t o D u r a t i o n ( ) ) ; I n t e r v a l gap = t h i r d M e e t i n g . gap ( lunchTime ) ; Érdekes az is, hogy mekkora az átfedés, amit az overlap() metódus számít ki nekünk: A periódusok használata Interval overlap = thirdMeeting .
overlap ( sportEvent ) ; Ez is intervallum, esetünkben 17-18 óráig. Kifinomult formázott kiírás A DateTimeFormatter class biztosítja a rugalmas, szöveges dátum előállítást. Néhány példa a formázó objektumok előállítására: DateTimeFormatter yyyy /MMM/ dd" ) DateTimeFormatter FRENCH) ; DateTimeFormatter GERMAN) ; fmt = DateTimeFormat . f o r P a t t e r n ( "å ; f r e n c h F m t = fmt . w i t h L o c a l e ( L o c a l e å germanFmt = fmt . w i t h L o c a l e ( L o c a l e å Egy dátumhoz így használjuk ezt a formázót: DateTime dateTime = new DateTime ( 2 0 1 2 , 1 2 , 1 , 1 2 , 1 5 , å DateTimeZone .UTC) ; System . o u t p r i n t l n ( germanFmt p r i n t ( dateTime ) ) ; Hatékonyan állíthatunk elő formázó objektumokat a DateTimeFormatterBuilder osztály bevetésével: DateTimeFormatter f o r m a t t e r = new å DateTimeFormatterBuilder ( ) . appendDayOfWeekShortText ( ) . appendLiteral ( " , ␣" ) . appendDayOfMonth (
2 ) . a p p e n d L i t e r a l ( ’− ’ ) . appendMonthOfYearShortText ( ) . a p p e n d L i t e r a l ( ’− ’ ) . appendYear ( 4 , 4 ) . appendLiteral ( " , ␣" ) . appendEraText ( ) . toFormatter ( ) ; Az időtartamok összehasonlítása Egy Interval példánynak van időtartama, ezért eszerint összehasonlíthatjuk az intervallumokat. Az alábbiakban i1 és i2 egy-egy intervallum: DateTime t o d a y = new DateTime ( DateTimeZone .UTC) ; Interval i 1 = new I n t e r v a l ( new LocalTime ( 9 , 0 ) . toDateTime ( t o d a y ) , new LocalTime ( 1 1 , 0 ) . toDateTime ( t o d a y ) ) ) ; Interval i 2 = new I n t e r v a l ( new LocalTime ( 2 0 , 0 ) . toDateTime ( t o d a y ) , new LocalTime ( 2 3 , 0 ) . toDateTime ( t o d a y ) ) ) ; Az alábbi példa 2012.0201 időpontra havi periódust használva 1 egységgel (azaz 1 hónappal) növeli meg a dátumot: public c l a s s DateTimeTest { . public s t a t i c void t e s t P e r i o d u s ( ) { D a t e M i d n i g
h t f i r s t O f F e b r u a r y = new D a t e M i d n i g h t å ( 2 0 1 2 , 2 , 1 , DateTimeZone .UTC) ; DateTimeFormatter f o r m a t t e r = ISODateTimeFormat . å yearMonthDay ( ) ; } . } // Eredmény : 2012−03−01 System . o u t p r i n t l n ( f o r m a t t e r p r i n t ( f i r s t O f F e b r u a r y å . p l u s ( Months ONE) ) ) ; A következő példa egy nagyon általános periódus létrehozását mutatja be: DateTimeFormatter f o r m a t t e r = ISODateTimeFormat . å yearMonthDay ( ) ; D a t e M i d n i g h t s t a r t = new D a t e M i d n i g h t ( 2 0 1 3 , 1 , 1 , å DateTimeZone .UTC) ; D a t e M i d n i g h t end = new D a t e M i d n i g h t ( 2 0 1 4 , 8 , 1 , å DateTimeZone .UTC) ; Period p e r i o d = new P e r i o d ( s t a r t , end ) ; D a t e M i d n i g h t s t a r t M i n u s P e r i o d = s t a r t . minus ( p e r i o d ) ; D a t e M i d n i g h t e n d P l u s P e r i o d = end . p l u s ( p e r i o d ) ; System . o u t p r i n t f ( "
b e f o r e ␣ s t a r t : ␣%s " . print ( startMinusPeriod ) ) ; System . o u t p r i n t f ( " p e r i o d ␣ s t a r t : ␣%s " print ( start ) ) ; System . o u t p r i n t f ( " p e r i o d ␣ end : ␣ ␣ ␣%s " p r i n t ( end ) ) ; System . o u t p r i n t f ( " a f t e r ␣ ␣ end : ␣ ␣ ␣%s " . print ( endPlusPeriod ) ) ; , formatter , formatter .å , formatter .å , formatter Az eredmény: b e f o r e s t a r t : 2011−06−01 p e r i o d s t a r t : 2013−01−01 p e r i o d end : 2014−08−01 after end : 2016−03−01 A periódus hossza: 1 év és 7 hónap. Ennek megfelelően az előző periódus 2011.0601-vel kezdődik, a következő pedig 20160301-én fejeződik be. 77 Java programozói könyvtár 6. Apache Commons - Virtual File System Apache Commons - Virtual File System Az Apache Virtual File System jelentőségét az adja, hogy a különféle fájlrendszereket elfedve biztosítja azok
egységes kezelését. Eközben megvalósítja a legfontosabb shell műveleteket is (másolás, mozgatás, könyvtár listázás, törlés). Bevezet egy eseménykezelő alrendszert is, amivel hatékony fájl alapú integrációt valósíthatunk meg. A project webhelye: http://commonsapacheorg/proper/ commons-vfs/. A VFS a következő fájlrendszereket támogatja: • Helyi fájlok és mappák (file:// ) • Tömörített fájlok: zip (zip:// ), (jar:// ), tar, tgz, bz2, gzip, bzip2 jar • Windows share (smb:// ) • FTP (ftp:// ) • HTTP, HTTPS (http:// ) • SSH vagy SCP (sftp:// ) • Webdav (webdav:// ) • Erőforrás (a class loader használatával) fájlok (res:// ) • Átmeneti fájlok (tmp:// ) Fájlműveletek Egy fájlrendszernek természetesen a fájlok a legfontosabb objektumai, amiket a VFS a FileObject osztállyal reprezentál. A dokumentáció tartalmaz egy Shelljava forrásprogramot ennek a bemutatására, amit most mi is felhasználunk az elemi műveletek
bemutatásához. A Shell osztály kódja 3 private adattagot tartalmaz, amit a példánkban mi is használunk: • private final FileSystemManager mgr; a VFS fájlrendszer menedzser 78 • private FileObject cwd; Az aktuális munkakönyvtár fájlobjektum (current working directory) • private BufferedReader reader; Egy reader textfájl kezeléshez Ezek inicializálása így történhet: p r i v a t e S h e l l ( ) throws F i l e S y s t e m E x c e p t i o n { mgr = VFS . getManager ( ) ; cwd = mgr . r e s o l v e F i l e ( System g e t P r o p e r t y ( " u s e r d i r "å )); r e a d e r = new B u f f e r e d R e a d e r (new I n p u t S t r e a m R e a d e r ( å System . i n ) ) ; } A cd parancs A change directory megvalósítását a következő kis részlet mutatja. A cd() metódus cmd tömbje tartalmazza a teljes parancsot. A cmd[0] mindig a parancs kulcsszava, utána jönnek a paraméterek A cd esetén csak 1 paraméternek van értelme, ami megadja, hogy mi
legyen az éppen aktuális, új munkakönyvtár útvonala. Amennyiben ez nincs megadva, akkor a lenti kód a user home directory-t állítja be a cwd értékeként. A manager resolveFile() metódusa képes a FileObject objektumot visszaadni, ami a beállítandó új könyvtár típusú fájlra mutat. Amennyiben az létezik, úgy erre állítjuk a cwd -t is. public c l a s s S h e l l { p r i v a t e f i n a l F i l e S y s t e m M a n a g e r mgr ; p r i v a t e F i l e O b j e c t cwd ; private BufferedReader r e a d e r ; . public void cd ( f i n a l S t r i n g [ ] cmd ) throws å Exception { f i n a l S t r i n g path ; i f ( cmd . l e n g t h > 1 ) { Java programozói könyvtár } else { } . } } path = cmd [ 1 ] ; Apache Commons - Virtual File System . path = System . g e t P r o p e r t y ( " u s e r home" ) ; // Locate and v a l i d a t e t h e f o l d e r F i l e O b j e c t tmp = mgr . r e s o l v e F i l e ( cwd , path ) ; i f ( tmp . e x i s t s ( )
) { cwd = tmp ; } else { System . o u t p r i n t l n ( " F o l d e r ␣ d o e s ␣ n o t ␣ e x i s t : å ␣ " + tmp . getName ( ) ) ; } System . o u t p r i n t l n ( " C u r r e n t ␣ f o l d e r ␣ i s ␣ " + cwd å getName ( ) ) ; public void c a t ( f i n a l S t r i n g [ ] cmd ) throws å Exception { i f ( cmd . l e n g t h < 2 ) { throw new E x c e p t i o n ( "USAGE: ␣ c a t ␣<path>" ) ; } // Locate t h e f i l e // Példa : cwd == mgr . r e s o l v e F i l e (" f i l e : / /home/å i n y i r i ") ; f i n a l F i l e O b j e c t f i l e = mgr . r e s o l v e F i l e ( cwd , å cmd [ 1 ] ) ; . } } // Dump t h e c o n t e n t s t o System . out F i l e U t i l . w r i t e C o n t e n t ( f i l e , System o u t ) ; System . o u t p r i n t l n ( ) ; Az ls parancs A pwd parancs A könyvtári tartalom listázását azért is érdemes Az aktuális könyvtár kiírás, illetve lekérdezése az megérteni, mert
saját programjainkban is hasonelőző példa után már nagyon egyszerű, de azért lóan szerezhetjük majd be a könyvtári bejegyzéseket. A lenti implementáció a -R kapcsoló esenézzük! public c l a s s S h e l l tében (6-16 sorok) rekurzívan is be tudja járni { . az alkönyvtárakat, amely üzemmódot a recurpublic void pwd ( ) { sive logikai változó tartalmazza. A 18-26 sorok System . o u t p r i n t l n ( " C u r r e n t ␣ f o l d e r ␣ i s ␣ " + cwd å getName ( ) ) ; között megszerezzük azt a file nevű FileObject } . objektumot, ami a listázás gyökérpontja lesz. A } 34-44 sorok között egyszerűen kiírjuk a fájl nevét, ha az nem FileType.FOLDER típusú, azaz A cat parancs maga már nem tárol más fájlokra vonatkozó listát. Mindebben egy másik VFS utility osztály, a A fájl tartalmát írja ki a képernyőre. A példaFileContent segít Amennyiben az éppen feldolbeli file változó egy olyan FileObject-re mutat, gozás alatt lévő
FileObject objektum egy folder, aminek a könyvtár részét a cwd, a fájlnév részét úgy meghívunk rá egy listChildren() nevű (a pedig a cmd[1] String adja. A FileUtil a VFS kódja a 49-73 sorok között van) metódust, ami könyvtár egy beépített utility osztálya. akár rekurzívan is képes bejárni a directory részpublic c l a s s S h e l l { fát. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public c l a s s S h e l l { . public void l s ( f i n a l S t r i n g [ ] cmd ) throws F i l e S y s t e m E x c e p t i o n { i n t pos = 1 ; f i n a l boolean r e c u r s i v e ; i f ( cmd . l e n g t h > pos && cmd [ pos ] e q u a l s ( "−R" ) ) { r e c u r s i v e = true ; pos++; } else { 79 Java programozói könyvtár 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 } Apache Commons - Virtual File System recursive = false
; final FileObject f i l e ; i f ( cmd . l e n g t h > pos ) { f i l e = mgr . r e s o l v e F i l e ( cwd , cmd [ pos ] ) ; } else { f i l e = cwd ; } } . } 80 i f ( f i l e . getType ( ) == F i l e T y p e FOLDER) { // L i s t t h e c o n t e n t s System . out p r i n t l n ( " Co nt ent s ␣ o f ␣ " + f i l e getName ( ) ) ; l i s t C h i l d r e n ( f i l e , r e c u r s i v e , "" ) ; } else { // S t a t t h e f i l e System . out p r i n t l n ( f i l e getName ( ) ) ; f i n a l FileContent content = f i l e . getContent ( ) ; System . out p r i n t l n ( " S i z e : ␣ " + c o n t e n t g e t S i z e ( ) + " ␣ b y t e s " ) ; f i n a l DateFormat dateFormat = DateFormat . g e t D a t e T i m e I n s t a n c e ( DateFormat MEDIUM, å DateFormat .MEDIUM) ; f i n a l S t r i n g lastMod = dateFormat . f o r m a t (new Date ( c o n t e n t g e t L a s t M o d i f i e d T i m e ( ) ) ) ; System . out p r i n t l n (
" L a s t ␣ m o d i f i e d : ␣ " + lastMod ) ; } /∗ ∗ ∗ Lists the children of a f o l d e r . ∗/ private void l i s t C h i l d r e n ( f i n a l F i l e O b j e c t d i r , f i n a l boolean r e c u r s i v e , final String prefix ) throws F i l e S y s t e m E x c e p t i o n { final FileObject [ ] children = dir . getChildren () ; f o r ( i n t i = 0 ; i < c h i l d r e n . l e n g t h ; i ++) { final FileObject child = children [ i ] ; System . out p r i n t ( p r e f i x ) ; System . out p r i n t ( c h i l d getName ( ) getBaseName ( ) ) ; i f ( c h i l d . getType ( ) == F i l e T y p e FOLDER) { System . out p r i n t l n ( " / " ) ; if ( recursive ) { l i s t C h i l d r e n ( child , r e c u r s i v e , p r e f i x + "␣␣␣␣" ) ; } } else { System . out p r i n t l n ( ) ; } } } Java programozói könyvtár Apache Commons - Virtual File System A Selectors osztály a működést befolyásolja, ezért érdemes
röviden megnézni a lehetséges érAz ismert működés szerint ennek a parancsnak tékeit, mert más helyeken is használható: csak az a hatása, hogy frissíti a fájl utolsó módosításának dátumát. A működése a következő: • SELECT SELF : Csak a megadott alap public c l a s s S h e l l (base) fájlt vagy foldert szelektálja. A touch parancs { . . } • SELECT SELF AND CHILDREN : Hasonló az előzőhöz, de a közvetlen gyerekeket is szelektálja. public void t o u c h ( f i n a l S t r i n g [ ] cmd ) throws å Exception { i f ( cmd . l e n g t h < 2 ) { throw new E x c e p t i o n ( "USAGE: ␣ t o u c h ␣<path>" ) å ; } f i n a l F i l e O b j e c t f i l e = mgr . r e s o l v e F i l e ( cwd , å cmd [ 1 ] ) ; if (! f i l e . exists () ) { f i l e . createFile () ; } f i l e . g e t C o n t e n t ( ) s e t L a s t M o d i f i e d T i m e ( System å currentTimeMillis () ) ; } • SELECT CHILDREN : Csak a közvetlen gyerekeket
szelektálja. • EXCLUDE SELF : Az összes gyereket szelektálja, kivéve magát a base foldert • SELECT FILES : Csak a „rendes” fájlokat szelektálja. A cp parancs • SELECT FOLDERS : Csak a foldereket szelektálja. A másolás az egyik legfontosabb művelet, így nézzük ennek is a megvalósítását! Az src változó • SELECT ALL: A base fájlt (foldert) és az a honnan kérdésre tárolja a választ. Az aktuális összes alkönyvtárát és fájlt is szelektál. könyvtárhoz képest tárolja el a másolandó fájl nevét, ami a szokások szerint az első paraméter, azaz a cmd[1] tartalma. A cmd[2] a cél nevét Az rm parancs reprezentálja. A copyFrom() metódus logikája Ez a parancs a fájl(ok) törlésére szolgál, ez a az, hogy ezt mindig a célfájl hívja meg magára, megvalósítása: megadva pataméterként a forrást. public c l a s s S h e l l { . public void cp ( f i n a l S t r i n g [ ] cmd ) throws å Exception { i f ( cmd . l e n g t h < 3 ) {
throw new E x c e p t i o n ( "USAGE: ␣ cp ␣<s r c >␣<d e s t å >" ) ; } f i n a l F i l e O b j e c t s r c = mgr . r e s o l v e F i l e ( cwd , å cmd [ 1 ] ) ; F i l e O b j e c t d e s t = mgr . r e s o l v e F i l e ( cwd , cmd [ 2 ] ) å ; i f ( d e s t . e x i s t s ( ) && d e s t getType ( ) == å F i l e T y p e .FOLDER) { d e s t = d e s t . r e s o l v e F i l e ( s r c getName ( ) å getBaseName ( ) ) ; } . } } d e s t . copyFrom ( s r c , S e l e c t o r s . SELECT ALL) ; public c l a s s S h e l l { . public void rm ( f i n a l S t r i n g [ ] cmd ) throws å Exception { i f ( cmd . l e n g t h < 2 ) { throw new E x c e p t i o n ( "USAGE: ␣rm␣<path>" ) ; } . } } f i n a l F i l e O b j e c t f i l e = mgr . r e s o l v e F i l e ( cwd , å cmd [ 1 ] ) ; f i l e . d e l e t e ( S e l e c t o r s SELECT SELF) ; Az mv parancs A fájl mozgatása nem valódi másolás és törlés művelet abban az
esetben, ha ugyanabban a fájlrendszerben történik. Miért? Mert ez ekkor csak 81 Java programozói könyvtár egy fájlnév átnevezést jelent. Ennek az az előnye, hogy tranzakcionális Különböző fájlrendszerek között egy copy és delete művelettel lehet megvalósítani, de ez sajnos nem tranzakcionális. A példa az első esetet mutatja be. Míg a copy a mit akarok „magamra” másolni, addig a move a „magamat” hova szeretném másolni szemléletű. public c l a s s S h e l l { . public void mv( f i n a l S t r i n g [ ] cmd ) throws å Exception { i f ( cmd . l e n g t h < 3 ) { throw new E x c e p t i o n ( "USAGE: ␣mv␣<s r c >␣<d e s t å >" ) ; } Apache Commons - Virtual File System agálás. A rendszerek integrálása vagy egy telepíthető új fájl megjelenése, törlése vagy módosítása esetén kívánatos az ilyen működés A VSF a következő 3 fájleseményt képes érzékelni és visszahívni a rájuk csatolt
listener kódját: • Új fájl jelent meg a figyelt könyvtárak valamelyikében (fileCreated()) • Egy fájl módosult (fileChanged()) • Egy fájlt töröltek (fileDeleted()) f i n a l F i l e O b j e c t s r c = mgr . r e s o l v e F i l e ( cwd , å cmd [ 1 ] ) ; F i l e O b j e c t d e s t = mgr . r e s o l v e F i l e ( cwd , cmd [ 2 ] ) å ; s r c . moveTo ( d e s t ) ; Ezen 3 esemény visszahívott kódját minden olyan Java objektum képes kezelni, ami imple} mentálja a FileListener VSF interfészt. A kö } vetkező kód a TestFileListener osztály segítségével mutat be egy lehetséges implementációt, amik esetünkben csak képernyőre való kiírások. Eseménykezelés A visszahívott metódusok egy FileChangeEvent A fájlok menedzselése során nagyon hasznos objektumot kapnak ajándékba, ez lehetővé teszi, szolgáltatás az eseményekre való automatikus re- hogy a kérdéses fájlt pontosan beazonosítsuk. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
21 22 package o r g . c s v f s ; import o r g . apache commons v f s 2 FileChangeEvent ; import o r g . apache commons v f s 2 F i l e L i s t e n e r ; public c l a s s T e s t F i l e L i s t e n e r implements F i l e L i s t e n e r { public void f i l e C h a n g e d ( FileChangeEvent e v e n t ) throws E x c e p t i o n { System . out p r i n t l n ( " V á l t o z o t t : ␣ " + e v e n t g e t F i l e ( ) getName ( ) ) ; } public void f i l e D e l e t e d ( FileChangeEvent e v e n t ) throws E x c e p t i o n { System . out p r i n t l n ( " T ö r ö l t : ␣ " + e v e n t g e t F i l e ( ) getName ( ) ) ; } } public void f i l e C r e a t e d ( FileChangeEvent e v e n t ) throws E x c e p t i o n { System . out p r i n t l n ( " L é t r e h o z o t t : ␣ " + e v e n t g e t F i l e ( ) getName ( ) ) ; } A következő FileEventProcessor program be- nitorozni a fájlrendszerben bekövetkező váltomutatja a VFS
eseménykezelést. A monitor zásokat, amire most a fenti TestFileListener nevű DefaultFileMonitor objektum képes mo- egy példányát akasztottuk rá eseménykezelő82 Java programozói könyvtár Apache Commons - Virtual File System ként. A monitoraddFile() képes egy listát fel- most csak 1 db ilyen van, ami a /home/xxx venni a megfigyelt könyvtárakról. Esetünkben mappa A monitorozás a start() metódussal indítható el, a stop()-pal pedig leállítható 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package o r g . c s v f s ; import j a v a . i o B u f f e r e d R e a d e r ; . public c l a s s F i l e E v e n t P r o c e s s o r { public s t a t i c void t e s t ( ) throws E x c e p t i o n { FileSystemManager fsManager = VFS . getManager ( ) ; F i l e t e s t F i l e = null ; D e f a u l t F i l e M o n i t o r mon itor = new D e f a u l t F i l e M o n i t o r ( new T e s t F i l e L i s t e n e r ( ) ) ; F i l e O b j e c t f
i l e O b j = fsManager . r e s o l v e F i l e ( " /home/ xxx " ) ; moni tor . s e t D e l a y ( 1 0 0 ) ; moni tor . a d d F i l e ( f i l e O b j ) ; moni tor . s t a r t ( ) ; Thread . s l e e p ( 6 0 0 0 0 ) ; } } moni tor . s t o p ( ) ; public s t a t i c void main ( S t r i n g [ ] a r g s ) throws E x c e p t i o n { test () ; } Az FTP protokoll Eddig főleg a lokális fájlrendszerrel foglalkoztun pedig a VFS nagy előnye, hogy eltakarja a programozó elől azokat a különbségeket, amiket értelmesen érdemes, így a fájlok kezelését megpróbálja egységesíteni. Ez azt jelenti, hogy egy File1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Object példány műveletei már függetlenek attól, hogy az ő fizikai fájlja hol van. Nézzük meg első példaként az FTP fájlrendszert, aminek a providerét az ismert Apache Common Net csomaggal (webhely: http://commons.apacheorg/ proper/commons-net/) implementálja a VFS. package o r g . c s v f s ; import import import
import import import import import import import import import import java . io F i l e ; o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 . AllFileSelector ; . FileObject ; . FileSelector ; . FileSystemException ; . FileSystemManager ; . FileSystemOptions ; . VFS ; . auth S t a t i c U s e r A u t h e n t i c a t o r ; . impl D e f a u l t F i l e M o n i t o r ; . impl D e f a u l t F i l e S y s t e m C o n f i g B u i l d e r ; . impl D e f a u l t F i l e S y s t e m M a n a g e r ; . provider sftp SftpFileProvider ; 83 Java programozói könyvtár 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 Apache Commons - Virtual
File System import o r g . apache commons v f s 2 p r o v i d e r s f t p S f t p F i l e S y s t e m C o n f i g B u i l d e r ; public c l a s s TestFTP { . public s t a t i c void t e s t F t p ( ) throws E x c e p t i o n { D e f a u l t F i l e S y s t e m M a n a g e r manager = new D e f a u l t F i l e S y s t e m M a n a g e r ( ) ; manager . a d d P r o v i d e r ( " f t p " , new F t p F i l e P r o v i d e r ( ) ) ; manager . i n i t ( ) ; S t r i n g u r l = " f t p : / / i n f n a v : ppppp@ftp . atw hu/ " ; F i l e S y s t e m O p t i o n s o p t s = new F i l e S y s t e m O p t i o n s ( ) ; F t p F i l e S y s t e m C o n f i g B u i l d e r . g e t I n s t a n c e ( ) s e t P a s s i v e M o d e ( op ts , true ) ; F i l e O b j e c t f t p F i l e = manager . r e s o l v e F i l e ( u r l , o p t s ) ; F i l e S e l e c t o r f s = new A l l F i l e S e l e c t o r ( ) ; FileObject [ ] children = ftpFile . findFiles ( fs ) ; System . out
p r i n t l n ( " C h i l d r e n ␣ o f ␣ " + f t p F i l e getName ( ) getURI ( ) ) ; f o r ( i n t i = 0 ; i < c h i l d r e n . l e n g t h ; i ++) { System . out p r i n t l n ( c h i l d r e n [ i ] getName ( ) getBaseName ( ) ) ; } manager . c l o s e ( ) ; } . } // end c l a s s A fenti példa 23. sorában saját manager példányt készítünk, majd beregisztráljuk ide az ftp protokollt. Az url szerkezete világos Nem kötelező, de mi a passive módot is beállítottuk, mert sok helyen szükséges a használata. A 33 sorban lekértük a „/ ” mappa összes bejegyzését, majd kilistáztuk azokat. Soha ne felejtsük el a 25 sor init() és a 41 sor close() metódusát meghívni, amikor saját manager-t használunk. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Az SFTP protokoll Az SFTP használata hasonló, de itt az SftpFileProvider osztály implementációját a JSch csomaggal (webhelye: http://www.jcraftcom/ jsch/) valósítja meg a VFS. A 27 sort
azért érdemes megemlíteni, mert az bemutatja hogyan kell kikapcsolni a host, azaz a fájlszerver tanúsítvány ellenőrzését. package o r g . c s v f s ; import import import import import import import import import import import import import import java . io F i l e ; o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons public c l a s s TestSFTP 84 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 . AllFileSelector ; . FileObject ; . FileSelector ; . FileSystemException ; . FileSystemManager ; . FileSystemOptions ; . VFS ; . auth S t a t i c U s e r A u t h e n t i c a t o r ; . impl D e f a u l t F i l e M o n i t o r ; . impl D e f a u l t F i l e S y s t e m C o n f i g B u i l d e
r ; . impl D e f a u l t F i l e S y s t e m M a n a g e r ; . provider sftp SftpFileProvider ; . provider sftp SftpFileSystemConfigBuilder ; Java programozói könyvtár 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 Apache Commons - Virtual File System { . public s t a t i c void t e s t S F t p ( ) throws E x c e p t i o n { S t r i n g u r l = " s f t p : / / i n y i r i : i n y i r i 1 2 @ e a i d e v s e r v / svn−repo−co " ; D e f a u l t F i l e S y s t e m M a n a g e r manager = new D e f a u l t F i l e S y s t e m M a n a g e r ( ) ; manager . a d d P r o v i d e r ( " s f t p " , new S f t p F i l e P r o v i d e r ( ) ) ; F i l e S y s t e m O p t i o n s o p t s = new F i l e S y s t e m O p t i o n s ( ) ; S f t p F i l e S y s t e m C o n f i g B u i l d e r . g e t I n s t a n c e ( ) s e t S t r i c t H o s t K e y C h e c k i n g ( op ts , " no " ) ; manager . i n i t ( ) ; F i l e O b j e c t
f t p F i l e = manager . r e s o l v e F i l e ( u r l ) ; System . out p r i n t l n ( f t p F i l e e x i s t s ( ) ) ; F i l e S e l e c t o r f s = new A l l F i l e S e l e c t o r ( ) ; FileObject [ ] children = ftpFile . findFiles ( fs ) ; f o r ( i n t i = 0 ; i < c h i l d r e n . l e n g t h ; i ++) { System . out p r i n t l n ( c h i l d r e n [ i ] getName ( ) getBaseName ( ) ) ; } manager . c l o s e ( ) ; } . } // end c l a s s mons Codec segítségével implementálja a környezet (webhelyek: http://hc.apacheorg/ A következő példaként tekintsük a HTTP prohttpclient-3.x/ és http://commonsapache tokollt, aminek HttpFileProvider osztályát az org/proper/commons-codec/). Apache Commons HTTP client 3.1 és a Com- HTTP protokoll 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package o r g . c s v f s ; import import import import import import import import import import import import import import java . io F i l e ; o r
g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons o r g . apache commons vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 vfs2 . AllFileSelector ; . FileObject ; . FileSelector ; . FileSystemException ; . FileSystemManager ; . FileSystemOptions ; . VFS ; . auth S t a t i c U s e r A u t h e n t i c a t o r ; . impl D e f a u l t F i l e M o n i t o r ; . impl D e f a u l t F i l e S y s t e m C o n f i g B u i l d e r ; . impl D e f a u l t F i l e S y s t e m M a n a g e r ; . provider sftp SftpFileProvider ; . provider sftp SftpFileSystemConfigBuilder ; public c l a s s TestHTTP { . public s t a t i c void testHTTP ( ) throws E x c e p t i o n { // h t t p : / / [ username [ : password ]@] hostname [ : p o r t ] [ a b
s o l u t e −p a t h ] S t r i n g u r l = " h t t p : / / svn . gep hu/ i n f o 2 h t m l c s s " ; // S t r i n g u r l = " h t t p s : / / sapkapu " ; D e f a u l t F i l e S y s t e m M a n a g e r manager = new D e f a u l t F i l e S y s t e m M a n a g e r ( ) ; manager . a d d P r o v i d e r ( " h t t p " , new H t t p F i l e P r o v i d e r ( ) ) ; 85 Java programozói könyvtár 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 Apache Commons - Virtual File System // manager . a d d P r o v i d e r (" h t t p s " , new H t t p F i l e P r o v i d e r ( ) ) ; manager . i n i t ( ) ; F i l e O b j e c t h t t p F i l e = manager . r e s o l v e F i l e ( u r l ) ; byte [ ] bs = F i l e U t i l . g e t C o n t e n t ( h t t p F i l e ) ; InputStream i s = new ByteArrayInputStream ( bs ) ; B u f f e r e d R e a d e r br = new B u f f e r e d R e a d e r (new InputStreamReader ( i s ) ) ; System . out p r i n t l n ( br
r e a d L i n e ( ) ) ; System . out p r i n t l n ( br r e a d L i n e ( ) ) ; br . c l o s e ( ) ; manager . c l o s e ( ) ; } . } // end c l a s s A fenti kódban semmi nehéz nincs, a 29. sorban hozzáadjuk a manager-hez a http protokollt. A 35 sorban a FileUtil segítségével egy mozdulattal áttesszük a fájl tartalmát egy bs nevű byte tömbbe, majd erre egy InputStream példányt, utána az ezt transzformáló BufferedReader objektumot kreálunk. Ez utóbbival már soronként tudjuk olvasni az áthozott info2html.css fájlt A VFS a webdav protokollt az Apache Jackrabbit csomaggal (webhelye: http://jackrabbitapacheorg/) implementálja A JAR fájl olvasása Példaként tekintsük ezt a kis tesztprogramot: public s t a t i c void t e s t J a r ( ) throws E x c e p t i o n { F i l e S y s t e m M a n a g e r f s M a n a g e r = VFS . getManager ( ) ; F i l e O b j e c t j a r F i l e = fsManager . r e s o l v e F i l e ( " j a r : / / / å home/ xxx / t e s t . j a r
" ) ; FileObject [ ] children = j a r F i l e . getChildren () ; System . o u t p r i n t l n ( " C h i l d r e n ␣ o f ␣ " + j a r F i l e getNameå ( ) . getURI ( ) ) ; gzip, bzip2 ) ezzel a módszerrel kezelni tudunk. Összefoglalás Amikor sokféle és esetleg távoli fájlokat kezelünk vagy kell egy eseménykezelő, akkor érdemes mindig a VFS-re gondolni és megvizsgálni, hogy segítségével támogatható-e a megoldás. Ez a példa például egy s3 protokollt használ, amivel az (webhely: https://code. google.com/p/vfs-s3/) Amazonnal kommunikálhatunk A protokollról többet itt olvashatunk: http://awsamazoncom/s3/ // Create b u c k e t F i l e S y s t e m M a n a g e r f s M a n a g e r = VFS . getManager ( ) ; F i l e O b j e c t d i r = f s M a n a g e r . r e s o l v e F i l e ( " s 3 : / / simpe−å bucket " ) ; dir . createFolder () ; // Upload f i l e t o S3 F i l e O b j e c t d e s t = f s M a n a g e r . r e s o l v e F i l e (
" s 3 : / / t e s t −å b u c k e t / backup . z i p " ) ; F i l e O b j e c t s r c = f s M a n a g e r . r e s o l v e F i l e (new F i l e ( " / pathå / to / l o c a l / f i l e . zip " ) getAbsolutePath ( ) ) ; d e s t . copyFrom ( s r c , S e l e c t o r s SELECT SELF) ; Az s3 provider (vfs-s3-bin.jar ) pedig innen tölthető le: https://codegooglecom/ p/vfs-s3/downloads/list. Tekintettel arra, } hogy a forrásprogram is elérhető, innen is tanulEz egy nagyon kellemes lehetőség, mert min- mányozhatjuk, hogy egy új provider-t miképpen den csomagolt fájltípust (zip, jar, tar, tgz, tbz2, érdemes elkészíteni. f o r ( i n t i = 0 ; i < c h i l d r e n . l e n g t h ; i ++) { System . o u t p r i n t l n ( c h i l d r e n [ i ] getName ( ) å getBaseName ( ) ) ; } 86 Java programozói könyvtár 7. Apache Commons - FileUpload Apache Commons - FileUpload Számos webalkalmazásban igény merül fel arra, hogy egy fájlt tölthessünk fel
a böngésző segítségével, amit majd a későbbiekben fel kell dolgozni. Jó példa erre egy browser alapú e-mail kliens, amikor a mellékelt dokumentumokat is ilyen módszerrel töltjük fel. Az itt felmerülő technikai feladatok könnyítésére szolgál a FileUpload könyvtár. Ebben a cikkben létrehozunk egy webalkalmazást (a neve MyWebProject lesz) és ezen keresztül bemutatjuk a FileUpload könyvtár használatát. Az Eclipse fejlesztői környezetet fogjuk használni, amiben egy új Dynamic Web Project-et hozunk létre Runtime környezetként az Apache Tomcat 6.x lett kiválasztva A 71 ábra a projekthez bepipált komponenseket, azaz Project Facet-eket mutatja. 7.1 ábra: Project Facets commons-fileupload) • Apache Commons IO könyvtár (webhely: http://commons.apacheorg/proper/ commons-io/) 7.2 ábra: Uploadjsp az editorban A példa alkalmazásunkhoz még a következő 2 jar hozzáadása is szükséges: Adjunk hozzá egy JSP lapot a projekthez, amit Upload.jsp
néven mentettünk el (7-1 Prog• Apache Commons FileUpload könyvtár ramlista) Ahogy azt a 72 ábráról is sejthet(webhely: commonsapacheorg/proper/ jük, az Eclipse meglehetősen fejlett eszközökkel 87 Java programozói könyvtár Apache Commons - FileUpload rendelkezik a html /jsp lap szerkesztést illetően. tos: Egyszerre láthatjuk és szerkeszthetjük a vizuális és forráskód nézetet is.Az Uploadjsp kódja egy• Az action attribútumnál megadtuk a nemszerű, tartalmaz 2 fájlfeltöltő html vezérlőt, amisokára ismertetésre kerülő FileUploadServvel egyszerre 2 fájl feltöltését is kezdeményezlet feltöltő servletet, mint azt az URL-t, hetjük. Ezenfelül természetesen van egy submit ahova a formot el kel post-olni. gomb is. A címléket a for kulcsszóval rendeltük a megfelelő input komponenshez. A 11 sorban • Megadtuk az enctype értékét helyesen, hilévő form tag szerepe több szempontból is fonszen fájlt szeretnénk feltölteni. 1 2 3 4 5
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // 7 −1. P r o g r a m l i s t a : Upload j s p <%@ page l a n g u a g e=" j a v a " contentType=" t e x t / html ; ␣ c h a r s e t=UTF−8" pageEncoding="UTF−8"%> < !DOCTYPE html PUBLIC "−//W3C//DTD␣HTML␣ 4 . 0 1 ␣ T r a n s i t i o n a l //EN" " h t t p : / /www w3 o r g /TR/ html4 / l o o s e å . dtd "> <html> <head> <meta http−e q u i v=" Content−Type" content=" t e x t / html ; ␣ c h a r s e t=UTF−8"> < t i t l e>Upload page</ t i t l e> </head> <body> <form action=" /MyWebProject/ F i l e U p l o a d S e r v l e t " method=" p o s t " e n c t y p e=" m u l t i p a r t / form−data "> <f i e l d s e t> <legend>F i l e f e l t ö l t é s e</ legend> <l a b e l f o r=" f i l e n a m e 1 ">F i l e : </ l a b e
l> <input id=" f i l e n a m e 1 " type=" f i l e " name=" f i l e n a m e 1 " s i z e=" 50 " value=" B ö n g é s z é s " /> <br/> <l a b e l f o r=" f i l e n a m e 2 ">F i l e : </ l a b e l> <input id=" f i l e n a m e 2 " type=" f i l e " name=" f i l e n a m e 2 " s i z e=" 50 " value=" B ö n g é s z é s " /> <br /> å <br/> <input type=" submit " value=" F e l t ö l t é s " /> </ f i e l d s e t> </form> </body> </html> A formon a FileUploadServlet class (7-2. Programlista) kódjára hivatkoztunk, így most nézzük meg alaposan. A forrásprogram több olyan részt is tartalmaz, amire nem megy a vezérlés, de oktatási céllal betettük ezeket is, hogy lássa az olvasó a feldolgozás lehetőségeit is. A kód jelenleg ezeket a részeket nem használja, azaz
nem megy rá soha a vezérlés: • 157-161. metódus sorok: processFileInMemory() A fenti 4 programrészlet bármikor felhasználható, azonban mi most csak a fájl feltöltés a lokális fájlrendszerbe funkciót aktivizáltuk (144148. sorok saveFile() metódusa) A submit gomb megnyomására a 62. sornál kezdődő process() metódus indul el A 65-81 sorok között • 36-51. sorok, azaz a newDiskFileItemFac- egy progressListener nevű változót hozunk létre, tory() metódus ami nem szükséges egy fájl feltöltéshez, de lehe• 134-142. sorok: processUploadedFile() me- tővé teszi, hogy annak előrehaladását megjeleníthessük Ez úgy valósul majd meg, hogy a tódus 102. sorban regisztráltuk ezt a listener-t, így az • 150-155. sorok: processFileAsStream() időnként visszahívódik, amellyel az ekkor lefutó metódus kód a monitorozás lehet. Mindez – ahogy már 88 Java programozói könyvtár említettük – csak opcionális elem. A 84 sorban létrehozott
factory fogja előkészíteni a feltöltési folyamat feldolgozását. A 89-90 sorokban a feltöltéshez használt átmeneti lokális könyvtárat adtuk meg, amit most a servlet környezeti paraméteréből nyerünk ki (a változó és az értéke a web.xml fájlban van definiálva) A 96 sor upload változója fogja vezérelni magát a feltöltés feldolgozást, így ennek a szerepe kulcsfontosságú. A 105 sorból pedig azt tanulhatjuk meg, 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 Apache Commons - FileUpload hogy az egyes FileItem objektumok bekerülnek egy listába, amit utána már könnyen fel tudunk dolgozni. Ez a feldolgozás a 110-125 sorok között történik, ami azt jelenti, hogy végigmegyünk az items listán és ha az nem form elem, hanem egy küldött fájlt reprezentál, akkor elmentjük a lokális fájlrendszerbe. Itt jegyezzük meg, hogy a kódban az item.getName() hívás
a fájl eredeti nevét adja vissza, így ezzel is mentjük el. // 7−2. P r o g r a m l i s t a : F i l e U p l o a d S e r v l e t j a v a package o r g . c s f i l e u p l o a d ; import import import import import import import import import import import import import import import import import import java . io F i l e ; j a v a . i o IOException ; j a v a . i o InputStream ; java . u t i l I t e r a t o r ; java . u t i l List ; javax . s e r v l e t ServletContext ; javax . s e r v l e t ServletException ; javax . s e r v l e t http HttpServlet ; javax . s e r v l e t http HttpServletRequest ; javax . s e r v l e t http HttpServletResponse ; o r g . apache commons f i l e u p l o a d F i l e I t e m ; o r g . apache commons f i l e u p l o a d F i l e I t e m F a c t o r y ; o r g . apache commons f i l e u p l o a d F i l e U p l o a d E x c e p t i o n ; o r g . apache commons f i l e u p l o a d P r o g r e s s L i s t e n e r ; o r g . apache
commons f i l e u p l o a d d i s k D i s k F i l e I t e m F a c t o r y ; o r g . apache commons f i l e u p l o a d s e r v l e t F i l e C l e a n e r C l e a n u p ; o r g . apache commons f i l e u p l o a d s e r v l e t S e r v l e t F i l e U p l o a d ; o r g . apache commons i o F i l e C l e a n i n g T r a c k e r ; public c l a s s F i l e U p l o a d S e r v l e t extends H t t p S e r v l e t { private s t a t i c f i n a l long s e r i a l V e r s i o n U I D = 1L ; public s t a t i c i n t KB = 1 0 2 4 ; public s t a t i c i n t MB = 1024 ∗ 1 0 2 4 ; public F i l e U p l o a d S e r v l e t ( ) { super ( ) ; } /∗ ∗ ∗ ∗ @param c o n t e x t ∗ @param r e p o s i t o r y ∗ @return ∗/ public s t a t i c D i s k F i l e I t e m F a c t o r y n e w D i s k F i l e I t e m F a c t o r y ( ServletContext context , F i l e r e p o s i t o r y ) { FileCleaningTracker fileCleaningTracker = FileCleanerCleanup . getFileCleaningTracker ( context ) ; D
i s k F i l e I t e m F a c t o r y f a c t o r y = new D i s k F i l e I t e m F a c t o r y ( D i s k F i l e I t e m F a c t o r y .DEFAULT SIZE THRESHOLD, r e p o s i t o r y ) ; factory . setFileCleaningTracker ( fileCleaningTracker ) ; 89 Java programozói könyvtár 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 } Apache Commons - FileUpload return f a c t o r y ; /∗ ∗ ∗ ∗ @param r e q u e s t ∗ @param r e s p o n s e ∗ @throws S e r v l e t E x c e p t i o n ∗ @throws IOException ∗ @throws F i l e U p l o a d E x c e p t i o n ∗/ public void p r o c e s s ( H t t p S e r v l e t R e q u e s t r e q u e s t , H t t p S e r v l e t R e s p o n s e r e s p o n s e ) throws E x c e p t i o n { // Egy ú j p r o g r e s s l i s t e n e r P r o g r e s s L i s t e n e r p r o g r e s s L i s t e n
e r = new P r o g r e s s L i s t e n e r ( ) { public void update ( long pBytesRead , long pContentLength , i n t pItems ) { // System . o u t p r i n t l n ("We a r e c u r r e n t l y r e a d i n g item " + p It em s ) ; i f ( pContentLength == −1) { // System . o u t p r i n t l n (" So f a r , " + pBytesRead // + " b y t e s have been r e a d . " ) ; } else { // System . o u t p r i n t l n (" So f a r , " + pBytesRead + " o f " // + pContentLength + " b y t e s have been r e a d . " ) ; } } }; // Egy d i s k −b a s e d f i l e i t e m s f a c t o r y l é t r e h o z á s a D i s k F i l e I t e m F a c t o r y f a c t o r y = new D i s k F i l e I t e m F a c t o r y ( ) ; // Egy r e p o s i t o r y , ami b i z t o s í t j a a b i z t o n s á g o s á t m e n e t i h e l y e t ServletContext servletContext = this . ge tS er vl et Co n fi g ( ) getServletContext ( ) ; F i l e r e p o s i t o r y = ( F i l e ) s
e r v l e t C o n t e x t . g e t A t t r i b u t e ( " javax s e r v l e t context tempdir " ) ; factory . setRepository ( repository ) ; // Memóriahasználat k o r l á t f a c t o r y . s e t S i z e T h r e s h o l d ( 4 ∗ KB) ; // K e z e l i a f e l t ö l t é s t S e r v l e t F i l e U p l o a d upload = new S e r v l e t F i l e U p l o a d ( f a c t o r y ) ; // Max . e k k o r e l e h e t a f á j l (nem k ö t e l e z ő ) upload . s e t S i z e M a x ( 1 0 ∗ MB) ; // B e á l l í t a n i a p r o g r e s s l i s t e n e r −t upload . s e t P r o g r e s s L i s t e n e r ( p r o g r e s s L i s t e n e r ) ; // A r e q u e s t e l e m z é s e L i s t <F i l e I t e m > i t e m s = upload . p a r s e R e q u e s t ( r e q u e s t ) ; // −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // A f e l t ö l t ö t t t a r t a l o m f e l d o l g o z á s a //
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− I t e r a t o r <F i l e I t e m > i t e r = i t e m s . i t e r a t o r ( ) ; while ( i t e r . hasNext ( ) ) { F i l e I t e m item = i t e r . n e x t ( ) ; 90 Java programozói könyvtár 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 Apache Commons - FileUpload i f ( item . i s F o r m F i e l d ( ) ) { p r o c e s s F o r m F i e l d ( item ) ; } } } else { F i l e t o = new F i l e ( " /home/temp/ "+item . getName ( ) ) ; s a v e F i l e ( item , t o ) ; } // A form mezői private void p r o c e s s F o r m F i e l d ( F i l e I t e m item ) { S t r i n g name = item . getFieldName ( ) ; S t r i n g v a l u e = item . g e t
S t r i n g ( ) ; } // Néhány a d a t h o z v a l ó h o z z á j u t á s demója private void p r o c e s s U p l o a d e d F i l e ( F i l e I t e m item ) { S t r i n g f i e l d N a m e = item . getFieldName ( ) ; S t r i n g f i l e N a m e = item . getName ( ) ; S t r i n g contentType = item . getContentType ( ) ; boolean isInMemory = item . isInMemory ( ) ; long s i z e I n B y t e s = item . g e t S i z e ( ) ; } // E lm en tj ük a f á j l r e n d s z e r b e n private void s a v e F i l e ( F i l e I t e m item , F i l e t o ) throws E x c e p t i o n { item . w r i t e ( t o ) ; } // Amennyiben stream−k é n t a k a r j u k f e l d o l g o z n i private void p r o c e s s F i l e A s S t r e a m ( F i l e I t e m item ) throws E x c e p t i o n { InputStream uploadedStream = item . g e t I n p u t S t r e a m ( ) ; uploadedStream . c l o s e ( ) ; } // Memóriában v a l ó f e l d o l g o z á s módja private void p r o c e s s F i l e I n M e m o r y ( F i l e I t
e m item ) throws E x c e p t i o n { byte [ ] data = item . g e t ( ) ; } protected void doGet ( H t t p S e r v l e t R e q u e s t r e q u e s t , H t t p S e r v l e t R e s p o n s e r e s p o n s e ) throws S e r v l e t E x c e p t i o n , IOException { try { process ( request , response ) ; } catch ( E x c e p t i o n e ) { // TODO Auto−g e n e r a t e d c a t c h b l o c k e . printStackTrace () ; } } protected void doPost ( H t t p S e r v l e t R e q u e s t r e q u e s t , H t t p S e r v l e t R e s p o n s e r e s p o n s e ) throws S e r v l e t E x c e p t i o n , IOException { try 91 Java programozói könyvtár 180 181 182 183 184 185 186 187 188 { } } process ( request , response ) ; } catch ( E x c e p t i o n e ) { // TODO Auto−g e n e r a t e d c a t c h b l o c k e . printStackTrace () ; } Most, hogy megértettük a programot, nézzük meg a jelenleg nem használt, de bármikor bevethető további feldolgozási lehetőségeket is. Ezeket a
saveFile() helyéről, ahelyett hívhatnánk meg. A 150-155 sorok között lévő processFileAsStream() metódus azt mutatja meg, hogy a feltöltött fájl tartalmát miképpen kaphatjuk meg egy InputStream objektumként. Ez sokszor fontos, például gondoljunk arra, hogy egy excel fájlt 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 Apache Commons - FileUpload töltünk fel és HTML táblázatként akarjuk utána megjeleníteni. Ekkor a teljes fájlt így célszerű átvenni és az excel formátum kezelésére alkalmas objektumnak átadni. A 157-161 sorok közötti processFileInMemory() metódus azt mutatja be, hogy akár egyből egy byte tömbbe is berakhatjuk a feltöltött fájlt a további feldolgozáshoz. A 7-3. Programlista az alkalmazás webxml fájlját tartalmazza. // 7 −3. P r o g r a m l i s t a : web xml <?xml version=" 1 . 0 " e n c o d i n g="UTF−8" ?> <web−app x m l n s : x s
i=" h t t p : //www. w3 o r g /2001/XMLSchema−i n s t a n c e " xmlns=" h t t p : // j a v a . sun com/xml/ ns / j a v a e e " xmlns:web=" h t t p : // j a v a sun com/xml/ ns / j a v a e e /web−å app 2 5 . xsd " x s i : s c h e m a L o c a t i o n=" h t t p : // j a v a . sun com/xml/ ns / j a v a e e ␣ h t t p : // j a v a sun com/xml/ ns / j a v a e e /web−å app 2 5 . xsd " i d="WebApp ID" version=" 2 . 5 "> <d i s p l a y −name>MyWebProject</ d i s p l a y −name> <welcome−f i l e − l i s t> <welcome− f i l e >i n d e x . j s p</ welcome− f i l e > </ welcome−f i l e − l i s t> < s e r v l e t> <s e r v l e t −name>Faces S e r v l e t</ s e r v l e t −name> <s e r v l e t −c l a s s>j a v a x . f a c e s webapp F a c e s S e r v l e t</ s e r v l e t −c l a s s> <lo ad −on−s t a r t u p>1</ loa d
−on−s t a r t u p> </ s e r v l e t> <s e r v l e t −mapping> <s e r v l e t −name>Faces S e r v l e t</ s e r v l e t −name> <u r l −p a t t e r n>/ f a c e s /∗</ u r l −p a t t e r n> </ s e r v l e t −mapping> <c o n t e x t −param> <param−name>j a v a x . s e r v l e t j s p j s t l fmt l o c a l i z a t i o n C o n t e x t</param−name> <param−v a l u e>r e s o u r c e s . a p p l i c a t i o n</param−v a l u e> </ c o n t e x t −param> <c o n t e x t −param> <param−name>j a v a x . f a c e s STATE SAVING METHOD</param−name> <param−v a l u e> c l i e n t</param−v a l u e> </ c o n t e x t −param> <c o n t e x t −param> <param−name>o r g . apache myfaces ALLOW JAVASCRIPT</param−name> 92 Java programozói könyvtár 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 <param−v a l u e>t r u e</param−v a l u e> </ c o n t e x t −param> <c o n t e x t −param> <param−name>o r g . apache myfaces PRETTY HTML</param−name> <param−v a l u e>t r u e</param−v a l u e> </ c o n t e x t −param> <c o n t e x t −param> <param−name>o r g . apache myfaces DETECT JAVASCRIPT</param−name> <param−v a l u e> f a l s e</param−v a l u e> </ c o n t e x t −param> <c o n t e x t −param> <param−name>o r g . apache myfaces AUTO SCROLL</param−name> <param−v a l u e>t r u e</param−v a l u e> </ c o n t e x t −param> < l i s t e n e r> < l i s t e n e r −c l a s s>o r g . apache myfaces webapp S t a r t u p S e r v l e t C o n t e x t L i s t e n e r</ l i s t e n e r −c l a s s> </ l i s t e n e r> < !−− l i s t e n e r
> < l i s t e n e r −c l a s s>o r g . apache commons f i l e u p l o a d s e r v l e t F i l e C l e a n e r C l e a n u p</ l i s t e n e r −c l a s s> </ l i s t e n e r −−> < s e r v l e t> <d e s c r i p t i o n></ d e s c r i p t i o n> <d i s p l a y −name>F i l e U p l o a d S e r v l e t</ d i s p l a y −name> <s e r v l e t −name>F i l e U p l o a d S e r v l e t</ s e r v l e t −name> <s e r v l e t −c l a s s>o r g . c s f i l e u p l o a d F i l e U p l o a d S e r v l e t</ s e r v l e t −c l a s s> <i n i t −param> <param−name>j a v a x . s e r v l e t c o n t e x t t e m p d i r</param−name> <param−v a l u e>/home/temp</param−v a l u e> </ i n i t −param> </ s e r v l e t> <s e r v l e t −mapping> <s e r v l e t −name>F i l e U p l o a d S e r v l e t</ s e r v l e t −name> <u r l −p a t
t e r n>/ F i l e U p l o a d S e r v l e t</ u r l −p a t t e r n> </ s e r v l e t −mapping> </web−app> A web.xml 59-61 sorában most megjegyzésbe tettük a FileCleanerCleanup listener osztály használatát Erre csak akkor van szükség, ha a DiskFileItem osztályt is használjuk, ami az alapértelmezett implementációja a FileItem class-nak. Ilyenkor célszerű a newDiskFileItemFactory() metódussal megszerezni a Disk1 2 3 4 5 6 7 Apache Commons - FileUpload FileItemFactory objektumot. Itt használható a org.apachecommonsioFileCleaningTracker, amit a következő pontban jobban meg is érthetjünk majd. Befejezésül még megadjuk a webalkalmazás induláskor generált indesjsp faces lapját, aminek az URL-je: http://localhost: 8080/MyWebProject/faces/index.jsp <%@ page l a n g u a g e=" j a v a " contentType=" t e x t / html ; c h a r s e t=UTF−8"%> <%@ t a g l i b u r i=" h t t p : / / j a v a . sun com/ j s f
/ html " p r e f i x="h"%> <%@ t a g l i b u r i=" h t t p : / / j a v a . sun com/ j s f / c o r e " p r e f i x=" f "%> <f : view> <html><head>< t i t l e><h : outputText value="Alma" /></ t i t l e> </head><body></body></html></ f : view> 93 Java programozói könyvtár 8. Apache Commons - IO Apache Commons - IO Az Apache Commons IO (webhely: http://commons.apacheorg/proper/commons-io/index html) azért született meg, hogy a Java programokban az IO kezeléséhez eszközöket adjon. Tartalmaz néhány statikus utility osztályt, támogatja a különféle input és output műveleteket, amikhez szűrőket is a rendelkezésünkre bocsát. Itt is található egy eseménykezelő alrendszer, ami lokális esetben jobb alternatíva lehet a VFS megoldásához képest is. Az IO könyvtár 6 fő részből áll: • Utility osztályok: Statikus metódusokkal támogat
néhány közhasznú feladatot. } } F i l e f i l e = new F i l e ( " Touch . d a t " ) ; F i l e U t i l s . touch ( f i l e ) ; } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } • Input: Hasznos InputStream és Reader implementációk. Egy fájl tartalmának Stringbe olvasása • Output: Hasznos OutputStream és Writer Tegyük fel, hogy adott egy szövegfájl, aminek a tartalmát 1 mozdulattal szeretnénk a meimplementációk. móriába tölteni, célszerűen egy String objek• Filterek: Különféle fájlszűrők. tumba. A következő példa readFileToString() • Comparator -ok: java.utilComparator metódusa mindezt megteszi, alapértelmezetten feltételezve, hogy a fájl tartalma UTF-8-ban van implementációk a fájlokhoz. kódolva. Ez utóbbi nem mindig teljesül, ezért • File Monitor: A fájlrendszer események opcionálisan a kódolást is megadhatjuk a Javaban szokásos módon. monitorozása package o r g . c s i o ; A FileUtils osztály
A fájl megérintése (touch) import o r g . a p a c h e commons i o F i l e U t i l s ; import j a v a . i o F i l e ; import j a v a . i o I O E x c e p t i o n ; . F i l e f i l e = new F i l e ( " s a m p l e . t x t " ) ; String content = F i l e U t i l s . readFileToString ( f i l e ) ; Bemelegítésként nézzük meg azt az egyszerű és S t r i n g ENCODING="UTF−8" ; g content = F i l e U t i l s . readFileToString ( f i l e , å ismert műveletet, amit a fájl megérintésének (to- S t r i nENCODING) ; . uch) hívunk és az a célja, hogy az utolsó módosítás dátumát a jelen pillanatra állítsa. A következő példa touch() metódusa mindezt megteszi, Egy String kiírása fájlba ha a fájl nem létezik, akkor üresen létrehozza Az előző feladat ellenkezője ugyanolyan gyakori azt. eset, ekkor egy String objektumot kell fájlba package o r g . c s i o ; menteni, amit a writeStringToFile() old meg. import o r g . a p a c h e commons i
o F i l e U t i l s ; Ennek is létezik olyan változata, amely egy megimport j a v a . i o F i l e ; import j a v a . i o I O E x c e p t i o n ; felelő kódkészlettel kódolva menti le a Stringet, public c l a s s TestTouch bár a modern környezetekben ennek már talán { public s t a t i c void main ( S t r i n g [ ] a r g s ) nem sok értelme van. A lehetőséget azonban { try { még támogatni kell, mert a régebbi rendszerek 94 Java programozói könyvtár Apache Commons - IO még nem unicode alapon működnek és a következő években sem várható itt jelentős előrelépés. String package o r g . c s i o ; import o r g . a p a c h e commons i o F i l e U t i l s ; import j a v a . i o F i l e ; import j a v a . i o I O E x c e p t i o n ; . S t r i n g d a t a = "Alma␣ a ␣ f a ␣ a l a t t " ; FileUtils . writeStringToFile ( f i l e , data ) ; S t r i n g ENCODING="UTF−8" ; FileUtils . writeStringToFile ( f i l e , data ,
ENCODING) ; Soronkénti írás-olvasás Az előzőekben a szövegfájl teljes tartalmát egy egységként kezeltük, pedig azt leggyakrabbak sorokból álló adatszerkezetnek képzeljük el. A readLines() képes arra, hogy ennek megfelelően egy List<String> objektumba töltse a sorokat, amit a következő példa be is mutat. package o r g . c s i o ; import import import import java . io F i l e ; java . i o IOException ; java . u t i l List ; o r g . a p a c h e commons i o F i l e U t i l s ; public c l a s s T e s t F i l e U t i l s { . public s t a t i c void r e a d L i n e s ( ) { F i l e f i l e = new F i l e ( " s a m p l e . t x t " ) ; try { L i s t <S t r i n g > c o n t e n t=F i l e U t i l s . r e a d L i n e s ( f i l e ) ; } . } for ( String l i n e : content ) { System . o u t p r i n t l n ( l i n e ) ; } } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } } . } Gyakori jelenség, hogy a fájlmásolást a
programozók újra és újra elkészítik, pedig az ilyen feladatokat érdemes olyan könyvtári rutinokra bízni, amik ezt már jól letesztelt módon tartalmazzák. Esetünkben a copyFile() metódus is megvalósítja, nézzük meg a használatát! A source a másolandó fájl, a target pedig a célfájl. Ez utóbbi persze nem létezik feltétlenül, hiszen alapvetően most akarjuk létrehozni. A targetDir egy könyvtárat reprezentál most, esetünkben ez az OS temp könyvtár, de ennek nincs semmi jelentősége a példa szempontjából. A copyFile() hívás után elkészül a fájl másolata. A copyFileToDirectory() pedig a source fájlt bemásolja ugyanilyen néven a targetDir könyvtárba. public c l a s s T e s t F i l e U t i l s { . public s t a t i c void t e s t F i l e C o p y ( ) { F i l e s o u r c e = new F i l e ( " j a n u a r y . doc " ) ; F i l e t a r g e t = new F i l e ( " j a n u a r y −backup . doc " ) ; F i l e t a r g e t D i r = new F i l
e ( System . g e t P r o p e r t y ( "å j a v a . i o tmpdir " ) ) ; try { package o r g . c s i o ; java . io F i l e ; java . i o IOException ; java . u t i l List ; o r g . a p a c h e commons i o F i l e U t i l s ; public c l a s s T e s t F i l e U t i l s { . public s t a t i c void w r i t e L i n e s ( ) { try { F i l e f i l e = new F i l e ( " s a m p l e . t x t " ) ; data ) ; Fájlok másolása Gyakorlatilag ennek az ellentétét lehet elérni a writeStringToFile() metódussal, ami egy új sort fűz a textfájlhoz. import import import import d a t a = " L e a r n i n g ␣ Java ␣ Programming " ; FileUtils . writeStringToFile ( f i l e , } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } . } } System . o u t p r i n t l n ( " Copying ␣ " + s o u r c e + "å ␣ f i l e ␣ to ␣" + t a r g e t ) ; F i l e U t i l s . copyFile ( source , target ) ; System . o u t p r i n t l n (
" Copying ␣ " + s o u r c e + "å ␣ f i l e ␣ to ␣" + t a r g e t D i r ) ; F i l e U t i l s . copyFileToDirectory ( source , å targetDir ) ; } catch ( I O E x c e p t i o n e ) { // Errors w i l l be r e p o r t e d h ere i f any å error occures during copying // t h e f i l e e . printStackTrace () ; } Teljes könyvtár másolása Egy speciálisabb eset – például archiválás esetén – egy teljes könyvtár másolása. Példánkban az 95 Java programozói könyvtár Apache Commons - IO srcDir a másolandó, a destDir könyvtár pedig a Másolás URL-ről célhely, ami létre lesz hozva. Magát a másolást a Egy hasznos lehetőség a HTTP URL-ről való copyDirectory() metódus végzi, amiről jegyezzük másolás közvetlen támogatása, ami a copyURLmeg az alábbiakat: ToFile() funkció személyében valósul meg. A forrás egy HTTP protokollon elérhető erőforrás, • a gyerek könyvtárakat is másolja a cél pedig egy helyi fájl,
ahova az URL-ről le• amikor nem létezik a célkönyvtár, azt lét- töltött byte-okat másoljuk. rehozza package example . commons i o ; import o r g . a p a c h e commons i o F i l e U t i l s ; • a fájl dátuma megmarad import j a v a . i o F i l e ; import j a v a . i o I O E x c e p t i o n ; import j a v a . n e t URL ; public c l a s s URLToFile { public s t a t i c void main ( S t r i n g [ ] a r g s ) { try { URL u r l = new URL( " h t t p : / / i n d e x . hu" ) ; F i l e d e s t i n a t i o n = new F i l e ( " i n d e x . html " ) ; package example . commons i o ; import o r g . a p a c h e commons i o F i l e U t i l s ; import j a v a . i o F i l e ; import j a v a . i o I O E x c e p t i o n ; public c l a s s D i r e c t o r y C o p y { public s t a t i c void main ( S t r i n g [ ] args ) { } F i l e U t i l s . copyURLToFile ( u r l , } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } S t r i n g s o u r c
e = " / alma / s o u r c e " ; F i l e s r c D i r = new F i l e ( s o u r c e ) ; S t r i n g d e s t i n a t i o n = " / alma / t a r g e t " ; F i l e d e s t D i r = new F i l e ( d e s t i n a t i o n ) ; } try { F i l e U t i l s . copyDirectory ( srcDir , } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } Könyvtárak törlése destDir ) ; destination ) ; Az alábbiakban az egyik példában bemutatjuk a teljes könyvtár, míg a másikban csak a könyvtár } tartalmának törlését. Az alábbi példa az elsőt szemlélteti. A törlés rekurzív, azaz az alkönyvtárak is törlődni fognak, hiszen annak nincs érTeljes könyvtár mozgatása telme, hogy csak a szülő szűnjön meg. Mi lenne a A könyvtárak mozgatása valószínűleg a másolás- gyerekekkel? Hiba esetén (például nincs jogunk nál is gyakoribb művelet, amikor az a cél, hogy a törlésre) IOException dobódik. az archivált adatok az eredeti helyen már ne je- public c
l a s s T e s t F i l e U t i l s lenjenek meg. Ilyenkor fontos, hogy a destDir { public s t a t i c void t e s t D e l e t e F i l e ( ) nem lehet már létező könyvtár. { } try { package example . commons i o ; import o r g . a p a c h e commons i o F i l e U t i l s ; import j a v a . i o F i l e ; import j a v a . i o I O E x c e p t i o n ; public c l a s s D i r e c t o r y M o v e { public s t a t i c void main ( S t r i n g [ ] a r g s ) { S t r i n g s o u r c e = " / alma / s o u r c e " ; F i l e s r c D i r = new F i l e ( s o u r c e ) ; . } S t r i n g d e s t i n a t i o n = " / alma / t a r g e t " ; F i l e d e s t D i r = new F i l e ( d e s t i n a t i o n ) ; } 96 } try { F i l e U t i l s . moveDirectory ( srcDir , } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } destDir ) ; } File d i r e c t o r y = new F i l e ( " /home/Temp/ Data "å ); FileUtils . deleteDirectory ( directory ) ; } catch ( I O
E x c e p t i o n e ) { e . printStackTrace () ; } A cleanDirectory() metódus nem törli a könyvtárat, azonban annak tartalmát igen. A következő példában a /home/xxx mappában létrehozunk 2 fájlt, majd letöröljük őket. Az egyik fájl ponttal kezdődik, ami UNIX esetén a rejtett fájl, erre is remekül működik az eljárás. Java programozói könyvtár public c l a s s T e s t F i l e U t i l s { . public s t a t i c void c l e a n D i r e c t o r y ( ) throws å IOException { F i l e path = new F i l e ( " /home/ xxx " ) ; F i l e U t i l s . t o u c h (new F i l e ( path , " alma " ) ) ; F i l e U t i l s . t o u c h (new F i l e ( path , " alma " ) ) ; } . } F i l e U t i l s . c l e a n D i r e c t o r y ( path ) ; szeretnénk, ezért létezik egy másik alakja is: C o l l e c t i o n <F i l e > l i s t F i l e s ( F i l e d i r e c t o r y , I O F i l e F i l t e r å fileFilter , IOFileFilter dirFilter ) ; Az
IOFileFilter az ismert Java szabványos interface-ek kiterjesztése, ezért ezeket mindenütt használhatjuk, ahol azokat is: • java.ioFileFilter Fájlok keresése A fájlok keresése általában azt jelenti, hogy egy megadott mappától indulva megadott tulajdonságokkal rendelkező fájlokat gyűjtünk össze, természetesen majd valamilyen későbbi feldolgozási céllal. Minderre a legegyszerűbb módszert talán az alábbi példában alkalmazott listFiles() metódus valósítja meg. Esetünkben a keresés kiinduló pontja (gyökere, azaz root) a /home/tanulas könyvtár Az extensions tömbben a keresett fájlkiterjesztéseket adtuk meg, azaz word, excel és powerpoint fájlokat szeretnénk találni. Amennyiben a recursive értékét true-val használjuk, úgy az almappákban is keresni fog a listFiles(), aminek az eseményét egy File gyűjteményben adja majd vissza. public c l a s s T e s t F i l e U t i l s { . public s t a t i c void s e a r c h F i l e s ( ) { F i l e r o
o t = new F i l e ( " /home/ t a n u l a s " ) ; try { String [ ] extensions = { " doc " , " x l s " , " ppt " }; boolean r e c u r s i v e = true ; C o l l e c t i o n <F i l e > f i l e s = F i l e U t i l s . l i s t F i l e s ( å root , extensions , r e c u r s i v e ) ; } . } Apache Commons - IO for ( F i l e f i l e : f i l e s ) { System . o u t p r i n t l n ( " F i l e ␣=␣ " + f i l e å getAbsolutePath ( ) ) ; } } catch ( E x c e p t i o n e ) { e . printStackTrace () ; } A fenti módon használt listFiles() persze nem tud túl sokat, általában ennél sokat többet • java.ioFilenameFilter A fenti metódus úgy működik, hogy a keresés a directory paraméterben specifikált könyvtártól indul. Azok a fájlok lesznek legyűjtve, amik a fileFilter szűrőn átmennek. Ilyen szűrő sok van, nemsokára részletezzük őket. A dirFilter paraméter opcionális. Amennyiben null értéket adunk ide, úgy a
keresés nem lesz rekurzív és csak a directory mappára korlátozódik. Ennek az az alternatívája, hogy az almappákban is keres a metódus, ekkor itt valamilyen könyvtár szűrőt kell megadnunk, amit szintén lentebb részletezünk. A filterek használata A Commons IO egy nagy IOFileFilter interfészt megvalósító gyűjteménnyel rendelkezik, mindenképpen fontos áttekinteni őket. A most következő példák célja, hogy bemutassa a szűrő objektumok konstruálásának tipikus módjait, azonban nem cél minden szűrő részletes bemutatása Nézzük meg először azt a szűrőt, ami a mappákon kívül mindent kiszűr, azaz a könyvtárakat engedi tovább. A neve a DirectoryFileFilter, aminek egy példányához a kódban bemutatott módon férhetünk hozzá. A listFiles() 3 paraméterében ilyet lehet megadni public s t a t i c void t e s t D i r e c t o r y F i l t e r ( ) { F i l e d i r = new F i l e ( " . " ) ; String [ ] f i l e s = dir . l i s t (
DirectoryFileFilter å INSTANCE) ; f o r ( i n t i = 0 ; i < f i l e s . l e n g t h ; i ++) { System . o u t p r i n t l n ( f i l e s [ i ] ) ; } } 97 Java programozói könyvtár Apache Commons - IO A következő kis forráskód a fájl utolsó módosítsa szerinti életkora alapján szűr, ezért AgeFileFilter a neve. A mintakódban azokat a fájlokat kérjük le, amelyek 1 napnál öregebbek. new A n d F i l e F i l t e r ( new A n d F i l e F i l t e r ( new P r e f i x F i l e F i l t e r ( "A" ) , new O r F i l e F i l t e r ( new S u f f i x F i l e F i l t e r ( " . c l a s s " ) , new S u f f i x F i l e F i l t e r ( " . j a v a " ) ) ), new N o t F i l e F i l t e r ( D i r e c t o r y F i l e F i l t e r . INSTANCE ) public s t a t i c void t e s t A g e F i l t e r ( ) { F i l e d i r = new F i l e ( " /home/ xxx " ) ; long c u t o f f = System . c u r r e n t T i m e M i l l i s ( ) − ( 2 4 ∗ 60 å ∗ 60 ∗ 1 0
0 0 ) ; S t r i n g [ ] f i l e s = d i r . l i s t (new A g e F i l e F i l t e r ( c u t o f f ) ) å ; } for ( int i = 0 ; i < f i l e s . length ; { System . o u t p r i n t l n ( f i l e s [ i ] ) ; } i ++) } ) ); f o r ( i n t i =0; i < f i l e s . l e n g t h ; i++ ) { System . o u t p r i n t l n ( f i l e s [ i ] ) ; } A további fontos filterek a itt csak egy felsorolásban adjuk meg: A WildcardFileFilter a fájl nevének mintázata alapján szűr. • NameFileFilter a fájl neve alapján szűr • RegexFileFilter reguláris kifejezést adhatunk meg a fájl nevére (példa: new RegexFileFilter("^.*[tT]est(\d+)?\.java$");) public s t a t i c void t e s t W i l d C a r d F i l e t e r ( ) { F i l e d i r = new F i l e ( " . " ) ; F i l e F i l t e r f i l e F i l t e r = new W i l d c a r d F i l e F i l t e r ( " ∗å t e s t ∗ . j a v a ~∗~" ) ; File [ ] f i l e s = dir . l i s t F i l e s ( f i l e F i l t e r ) ; f o r (
i n t i = 0 ; i < f i l e s . l e n g t h ; i ++) { System . o u t p r i n t l n ( f i l e s [ i ] ) ; } } • SizeFileFilter A fájl mérete legyen nagyobb a megadottnál. (példa: new SizeFileFilter(1024 * 1024); Az 1MB-nál nagyobb fájlokat engedi át) Ez már sokkal hatékonyabb feladatmegoldó filter, mint a bevezetőben megadott lehetőség, amit a SuffixFileFilter segítségével tudnánk legkönyebben megvalósítani. FileFilter ); FileFilter ; • CanReadFileFilter a fájl olvasható • CanWriteFileFilter a fájl írható f i l e F i l t e r 1 = new S u f f i x F i l e F i l t e r ( " . c l a s s "å f i l e F i l t e r 2 = new S u f f i x F i l e F i l t e r ( " . j a v a " ) å • FalseFileFilter csak a rendes fájlokat engedi tovább, a mappákat nem Az olvasó mire idáig ért bizonyára elgondolkodott azon, hogy a gyakorlatban általában több szűrő kompozíciója ad ki egy feladathoz illeszkedő szűrőt. Erre van megoldás,
ugyanis létetik 3 speciális filer, aminek pont ez a feladata: • EmptyFileFilter üres könyvtár vagy fájl • HiddenFileFilter a rejtett fájlok • FalseFileFilter semmit sem enged át a szűrőn • AndFileFilter Két szűrő ÉS logikai kapcsolata • TrueFileFilter mindent átenged a szűrőn • OrFileFilter Két szűrő VAGY logikai kapcsolata • NotFileFilter Egy szűrő tagadás A FileUtils egyéb lehetőségei Befejezésül áttekintjük a FileUtils class még nem Mindezek jobb megértése kedvéért tekintsük az említett, de hasznos további metódusait. Egy ezt bemutató példánkat! könyvtár teles mérete így kérdezhető le: public s t a t i c void t e s t A n d O r F i l t e r ( ) { F i l e d i r = new F i l e ( " . " ) ; String [ ] f i l e s = dir . l i s t ( 98 long s i z e =F i l e U t i l s . s i z e O f D i r e c t o r y (new F i l e ( " / o p t " ) ) ; Egy fájl mérete pedig így: Java programozói könyvtár F
i l e f i l e = new F i l e ( " /home/ v a l a m i . b i n " ) ; long s i z e = f i l e . l e n g t h ( ) ; S t r i n g d i s p l a y=F i l e U t i l s . b y t e C o u n t T o D i s p l a y S i z e ( s i z e ) ; Apache Commons - IO IOUtils Az IOUtils class a fájl↔memória változó közötti adatmozgatás támogató statikus metódusok gyűjteménye. A következőkben 2 példát Sokszor jól jön, ha a fájlról egy CRC szá- adunk erre. mot tudunk elmenteni és időnként azt visszaellenőrizni, amit a checksumCRC32() metódus Egy fájl tömbbe olvasása tesz meg: Egy fájl alacsonyabb szinten byte-ok rendezett F i l e f i l e = new F i l e ( " /home/ alma . t x t " ) ) ; long c r c = F i l e U t i l s . checksumCRC32 ( f i l e ) ; sorozata, emiatt az egyik legtermészetesebb művelet az, hogy ezeket a byte-okat egy byte[] Két fájl byte-ról byte-ra való egyezésének tömbbe beolvassuk. A Java InputStream egy vizsgálatát a contentEquals() szolgáltatja:
olyan interface, ami minden olyan objektum boolean c o n t e n t E q u a l s ( F i l e f i l e 1 , F i l e f i l e 2 ) throws å absztrakciója, amit byte-onként le lehet olvasni IOException ezen a csatornán. Persze a fájl is ilyen, így A contentEqualsIgnoreEOL() szöveg alapon a toByteArray() metódus ilyenből fogja olvasni teszi mindezt, azaz soronként teszi meg az össze- azokat, egyetlen hívással betéve a példában muhasonlítás. Emiatt a kódlap paramétert is meg tatott bytes változó által referált objektumba F i l e f i l e = new F i l e ( " / t e s t / r e s o u r c e s / H e l l o . t x t " ) ; kell neki adni. A convertFileCollectionToFileAr- try ray() metódus egy File[] tömböt ad vissza egy { I n p u t S t r e a m i s = new F i l e I n p u t S t r e a m ( f i l e ) ; byte [ ] b y t e s = I O U t i l s . t o B y t e A r r a y ( i s ) ; File kollekcióból. Az isFileNewer() egy család, System . o u t p r i n t l n ( " Byte ␣ a r r a y ␣ s
i z e : ␣ " + b y t e s å ahol többféleképpen tudunk időpontot megadni length ) ; } catch ( I O E x c e p t i o n e ) és az eredmény akkor lesz true, ha a fájl fiata- { e . printStackTrace () ; labb a megadott szempontnál. Az isFileOlder() } hasonló, de itt a fájlnak öregebbnek kell lennie. Unix alatt létezik a szimbolikus link, aminek igaz voltát az isSymlink() logikai metódussal lehet le- InputStream alakítása Stringgé kérdezni. A moveFile() fájlt tud mozgatni, ilyen A példában is egy olyan InputStream, amit egy a paraméterezése: fájlból származtattunk és olvasunk. A toString() void m o v e F i l e ( F i l e s r c F i l e , F i l e d e s t F i l e ) throws å metódus ezt egy hívással beteszi nekünk egy IOException Stringbe, persze a kódolás megadása elengedheVan 2 érdekes metódus: tetlen. System . o u t p r i n t l n ( "Name␣ ␣ ␣ ␣=␣ " + f i l e getName ( ) ) ; System . o u t p r i n t l n ( " s i z e ␣ ␣
␣ ␣=␣ " + s i z e ) ; System . o u t p r i n t l n ( " D i s p l a y ␣=␣ " + d i s p l a y ) ; package example . commons i o ; F i l e I n p u t S t r e a m o p e n I n p u t S t r e a m ( F i l e f i l e ) throws å IOException F i l e O u t p u t S t r e a m openOutputStream ( F i l e f i l e ) throws å IOException import o r g . a p a c h e commons i o I O U t i l s ; Az egyik egy input, a másik egy output stream byte csatornát képes nyitni az adott fájlra. A readFileToByteArray() egy byte[] objektumba másolja a fájl tartalmát A FileUtils class ezenkívül rendelkezik még számos, különféle helyzetekben jól használható write.() metódussal public c l a s s I n p u t S t r e a m T o S t r i n g { public s t a t i c void main ( S t r i n g [ ] a r g s ) { I n p u t S t r e a m i s = new F i l e I n p u t S t r e a m (new F i l e ( å " data . t x t " ) ) ; S t r i n g c o n t e n t s = I O U t i l s . t o S t r i n g ( i s ,
"UTF−8"å ); System . o u t p r i n t l n ( c o n t e n t s ) ; IOUtils . closeQuietly ( i s ) ; } } import j a v a . i o I n p u t S t r e a m ; import j a v a . i o F i l e I n p u t S t r e a m ; import j a v a . i o F i l e ; 99 Java programozói könyvtár Comparator implementációk A java.utilComparator egy interface, amit utána sok algoritmus képes használni, ahol 2 objektumot össze kell hasonlítani. Két fájl esetén sokféle értelme van az összehasonlításnak, emiatt az IO csomag implementál néhányat. Előtte azonban nézzünk 2 kis példát! Az első a fájlok mérete alapján rendez: List f i l e s L i s t = . // Obtain a l i s t o f f i l e s from å somewhere C o l l e c t i o n s . sort ( f i l e s L i s t , SizeFileComparator å SIZE COMPARATOR) ; Apache Commons - IO fájlt az utolsó módosítása szerint hasonlít össze. A NameFileComparator pedig a fájlok nevei alapján állít fel sorrendet, természetesen a lexikografikus
rendezést használva. A PathFileComparator is hasonló, de a fájl path-t veszi alapul A ExtensionFileComparator a fájlnév kiterjesztése alapján teszi mindezt A CompositeFileComparator azért jó, mert segítségével sorrendben egymás után több összehasonlítási szempont kombinálását is támogatja. A második pedig a könyvtárak tartalmának Séta a mappák között összmérete alapján a könyvtárakat. List d i r e c t o r i e s L i s t = . // Obtain a l i s t o f å d i r e c t o r i e s from somewhere C o l l e c t i o n s . sort ( d i r e c t o r i e s L i s t , SizeFileComparator å SIZE SUMDIR COMPARATOR) ; A DirectoryWalker<T> egy nagyon hasznos eszköz, mert segítségével olyan algoritmusokat impA fájlok világában milyen összehasonlítás ér- lementálhatunk, amik végigmennek egy könyvtelmes még? Nézzük meg ezt, hogy gondolja az tárhierarchián és annak minden elemén csinálIO csomag! A LastModifiedFileComparator() 2 hatnak valami
szükséges műveletet. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public c l a s s F i l e C l e a n e r extends D i r e c t o r y W a l k e r { public F i l e C l e a n e r ( ) { super ( ) ; } public L i s t c l e a n ( F i l e s t a r t D i r e c t o r y ) { L i s t r e s u l t s = new A r r a y L i s t ( ) ; walk ( s t a r t D i r e c t o r y , r e s u l t s ) ; return r e s u l t s ; } protected boolean h a n d l e D i r e c t o r y ( F i l e d i r e c t o r y , i n t depth , C o l l e c t i o n r e s u l t s ) { // d e l e t e svn d i r e c t o r i e s and t h e n s k i p i f ( " . svn " e q u a l s ( d i r e c t o r y getName ( ) ) ) { directory . delete () ; return f a l s e ; } else { return true ; } } } 100 protected void h a n d l e F i l e ( F i l e f i l e , i n t depth , C o l l e c t i o n r e s u l t s ) { // d e l e t e f i l e and add t o l i s t o f d e l e t e d f i l e . delete () ; r e s u l t s . add ( f i l e )
; } Java programozói könyvtár A FileCleaner osztály a használatot mutatja be, azaz mindig kell készítenünk egy utód osztályt és abban kell megvalósítani a funkcionalitást. A walk() metódus indítja el a sétát a startDirectory ponttól, a bejárt helyeket pedig a results listában kapjuk vissza. A séta során könyvtárakkal, fájlokkal találkozik a walker, miközben ezekre visszahívja az itt implementált, az osztályra speciális célú handleDirectory() és handleFile() fájl metódusokat. FileSystemUtils Apache Commons - IO long f r e e S p a c e K B = F i l e S y s t e m U t i l s . å f r e e S p a c e K b ( path ) ; long freeSpaceMB = f r e e S p a c e K B / F i l e U t i l s å .ONE KB; long f r e e S p a c e G B = f r e e S p a c e K B / F i l e U t i l s å .ONE MB; } } System . o u t p r i n t l n ( " S i z e ␣ o f ␣ " ␣ " + f r e e S p a c e K B + " ␣KB" ) System . o u t p r i n t l n ( " S i z e ␣ o f
␣ " ␣ " + freeSpaceMB + " ␣MB" ) System . o u t p r i n t l n ( " S i z e ␣ o f ␣ " ␣ " + f r e e S p a c e G B + " ␣GB" ) } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } + path + " ␣=å ; + path + " ␣=å ; + path + " ␣=å ; Eseménykezelés A fájlrendszer egészére vonatkozó rutinok kerül- Az IO könyvtár is biztosít egy fájlmonitort, tek ide. A példa kiírja a lemez szabad helyének amivel a különféle fájlrendszer eseményeket észlelni lehet és azokra visszahívható metódusok méretét: tehetők, amik az események kezelői. A lenti package o r g . example commons i o ; TestIOEvent class ennek a programozását muimport o r g . a p a c h e commons i o F i l e S y s t e m U t i l s ; import o r g . a p a c h e commons i o F i l e U t i l s ; tatja be egy nagyon egyszerű példán, ugyanis az import j a v a . i o I O E x c e p t i o n ; onXXX() visszahívható
metódusok implementápublic c l a s s D i s k F r e e S p a c e { ció csak kiírják azt, hogy egy esemény bekövetpublic s t a t i c void main ( S t r i n g [ ] a r g s ) { try { S t r i n g path = "C : " ; kezett. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package o r g . c s i o ; import import import import java . io F i l e ; o r g . apache commons i o moni tor F i l e A l t e r a t i o n L i s t e n e r ; o r g . apache commons i o moni tor F i l e A l t e r a t i o n M o n i t o r ; o r g . apache commons i o moni tor F i l e A l t e r a t i o n O b s e r v e r ; public c l a s s TestIOEvent { public s t a t i c void main ( S t r i n g [ ] a r g s ) throws E x c e p t i o n { F i l e d i r e c t o r y = new F i l e ( " /home/ xxx " ) ; F i l e A l t e r a t i o n O b s e r v e r o b s e r v e r = new F i l e A l t e r a t i o n O b s e r v e r ( d i r e c t o r y ) ; F i l e A l t e r a t i o n L i
s t e n e r l i s t e n e r = new F i l e A l t e r a t i o n L i s t e n e r ( ) { public void o n S t a r t ( F i l e A l t e r a t i o n O b s e r v e r f a o ) { System . out p r i n t l n ( " o n S t a r t : " + f a o t o S t r i n g ( ) ) ; } public void o n D i r e c t o r y C r e a t e ( F i l e f i l e ) { System . out p r i n t l n ( " o n D i r e c t o r y C r e a t e : " + f i l e getName ( ) ) ; } public void o n D i r e c t o r y C h a n g e ( F i l e f i l e ) { System . out p r i n t l n ( " o n D i r e c t o r y C h a n g e : " + f i l e getName ( ) ) ; } 101 Java programozói könyvtár 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 Apache Commons - IO public void o n D i r e c t o r y D e l e t e ( F i l e f i l e ) { System . out p r i n t l n ( " o n D i r e c t o r y D e l e t e : " + f i l e getName ( ) ) ; }
public void o n F i l e C r e a t e ( F i l e f i l e ) { System . out p r i n t l n ( " o n F i l e C r e a t e : " + f i l e getName ( ) ) ; long GAP = 1000 ∗ 1 0 ; long l a s t T i m e = f i l e . l a s t M o d i f i e d ( ) ; while ( f i l e . l a s t M o d i f i e d ( ) − l a s t T i m e <= GAP ) { try { Thread . s l e e p (GAP) ; } catch ( I n t e r r u p t e d E x c e p t i o n e ) { e . printStackTrace () ; } lastTime = f i l e . l a s t M o d i f i e d ( ) ; } } public void onFileChange ( F i l e f i l e ) { System . out p r i n t l n ( " onFileChange : " + f i l e getName ( ) ) ; } public void o n F i l e D e l e t e ( F i l e f i l e ) { System . out p r i n t l n ( " o n F i l e D e l e t e : " + f i l e getName ( ) ) ; } }; public void onStop ( F i l e A l t e r a t i o n O b s e r v e r f a o ) { System . out p r i n t l n ( " onStop : " + f a o t o S t r i n g ( ) ) ; } observer . addListener ( l i s t e n e r ) ;
} } F i l e A l t e r a t i o n M o n i t o r monit or = new F i l e A l t e r a t i o n M o n i t o r ( 1 0 0 0 ) ; moni tor . addObserver ( o b s e r v e r ) ; moni tor . s t a r t ( ) ; Thread . s l e e p ( 1 0 0 0 ∗ 6 0 ) ; moni tor . s t o p ( ) ; Az onFileCreate() azért lett egy kicsit több sorból álló, mert azt is lekezeli, hogy egy fájl legalább 10 másodperc öreg legyen. Erre azért lehet szükség, mert az onFileCreate() bejelenti egy új fájl megjelenését, de azzal már nem törődik, hogy módosul-e még. A példa egyébként a /home/xxx könyvtárban történteket figyeli, mert a monitort arra állítottuk. Az ismert observer design pattern valósítja meg a működést, amihez 102 több listener-t is tehetnék, de a példában most csak 1 van. Stream és Reader/Writer osztályok Az IO csomag tartalmaz néhány közhasznú • InputStream • Reader Java programozói könyvtár • OutputStream és Apache Commons - IO IOCase • Writer A
különféle rendszerek (Unix, Windows) eltérő implementációt olyan esetekre, amik gyakran érzékenységűek a kis és nagybetű különbségekre. előfordulnak a programozás során. Érdemes eze- Ezeknek az univerzálisabb kezeléséhez ad segítket röviden áttekinteni, mert nagyon jól jönnek, séget ez az osztály amikor éppen szükségünk van valamelyikükre. Mi itt példaképpen csak egyet, a LockableFileWriter osztályt mutatjuk be röviden, ami ezért jó, Charsets mert úgy működik, mint az ismert FileWriter, de létrehoz egy lock fájlt. A legegyszerűbb konst- Szabványos javaniocharsetCharset objektumokat képes szolgáltatni ruktora így néz ki: public L o c k a b l e F i l e W r i t e r ( F i l e IOException f i l e ) throws å Amikor meghívjuk a close() metódust, akkor FileCleaningTracker a zároló fájl is törlődik. Egyéb statikus osztályok HexDump A fájlok törlésre jelölését és annak törlését menedzseli egy hozzárendelt handler
objektum segítségével. Amikor ez a szemétgyűjtés során felszabadul, az így megjelölt fájl törlésre kerül Ez a kis osztály egy byte[] adatot hexadecimális formában küld el egy OutputStream csatornára. FileDeleteStrategy Egyetlen metódusa a dump(). A fájlok törlése során alkalmazott stratégiát reprezentáló példányokat létrehozó osztály. Például A FileUtils osztállyal való közös használatra lett mondhatjuk azt, hogy csak üres könyvtárat tötervezve, ahogy a következő példa is mutatja: rölhetünk, de azt is, hogy erőltetjük a nem üres L i n e I t e r a t o r i t = F i l e U t i l s . l i n e I t e r a t o r ( f i l e , "UTF−8"å könyvtárak törlését. ); LineIterator try { while ( i t . hasNext ( ) ) { String l i n e = i t . nextLine () ; // do something w i t h l i n e } } finally { it . close () ; } FilenameUtils A lineIterator() első paramétere egy File ob- Segítségével a fájlnevek és teljes útvonalak
manipulálását könnyíthetjük meg. Windows esetén jektum. egy név ilyen: C:devprojectfile.txt A C: a prefix. A devproject a path Az osztály meEndianUtils tódusai ezen név elemeknek a lekérdezését, ilA különféle unicode rendszerek közötti átjárást leszkedés vizsgálatát támogatják. Ilyen például segíti, amennyiben ilyenre szükségünk van, ér- a String getPath(String filename), ami a/b/c.txt demes ezt az osztályt is megnézni. esetén a a/b/ értéket adja vissza. 103 Java programozói könyvtár 9. Az Active Directory elérése Az Active Directory elérése Ebben a fejezetben Active Directory (AD) címtár adatbázis olvasását és írását tekintjük át. Régebben külön Java könyvtárak használatára volt szükség ehhez, de az Java 16 óta ezen funkcionalitás része a Java SDK-nak is. Szeretnénk megköszönni kollégánknak, Zilahy Zoltánnak, hogy felkeltette az érdeklődést ezen cikk megírására, amihez néhány jó ötletet is
adott. Az LDAP rövid áttekintése Az LDAP 3 protokoll egy alkalmazás rétegben lévő API, ami lehetővé teszi az elosztott címtár szerverek elérését és karbantartását. Az LDAP az IETF 4 szervezet szabványa, aminek az utolsó változata az RFC 4511. Egy Directory Service bármilyen rekordot képes a címtár fa struktúrájában eltárolni, ezeknek a rekordoknak az összességét a címtár sémájának nevezzük. Egy címtár alapja lehet egy vállalati SSO megoldásnak. Az LDAP rövid története Talán elsőként a távközlési vállalatok ismerték fel a címtárak jelentőségét, gondoljunk csak a telefonkönyvekre. Kialakult a címtárak koncepciója, amit végül az X500 szabványban rögzítettek, amely folyamatot az ITU 5 menedzselte az 1980-as években. Ezt a DAP protokollal lehetett elérni, de hamarosan megjelent ennek alternatívájaként az LDAP, aminek jelenlegi LDAPv3 változata először 1997-ben volt publikálva. Az LDAP áttekintése Az LDAP
kliens/szerver felépítésű, azaz a kliensek kapcsolódnak az LDAP szerverhez és utána hálózaton keresztül elérik annak szolgáltatásait. Érdemes megjegyezni, hogy a szerverek elterjedt alapértelmezett TCP portja a 389. A kliens különféle műveletek elvégzését kérheti a szervertől, amire az elvégzi a kért feladatot és valamilyen válasz üzenettel tudatja mindezt a hívó felé. Milyenek is lehetnek ezek a műveletek? Nézzük meg röviden: • Search Ez talán a leggyakoribb művelet típus, így az LDAP szerverek is erre optimalizáltak. A cél megtalálni egy directory bejegyzést (directory entry) és természetesen azt visszaadni a kliens részére. Ez lehet egy egész lista is, hiszen a keresési feltételnek több rekord is eleget tehet. • Compare Azt lehet ezzel a művelettel tesztelni, hogy egy megnevezett rekord tartalmazza-e megadott attribútum értéket. • Add Egy új rekord (entry) hozzáadása a címtárhoz. 3 LDAP=Lightweight Directory
Access Protocol Internet Engineering Task Force 5 International Telecommunication Union 4 104 Java programozói könyvtár Az Active Directory elérése • Delete Egy rekord (entry) törlése a címtárból. • Modify Egy rekord (entry) módosítása a címtárban. • DN Modify A Distinguished Name (DN, azaz megkülönböztető név) módosítása a címtárban. Ez lehet mozgatás vagy átnevezés • Abandon Egy előző kliens műveleti kérés megszakítása. • Bind Bejelentkezés, ami megadja a használni kívánt LDAP protokoll verziót is . • Unbind A kapcsolat lezárása. • StartTLS Az LDAPv3 biztonságos TLS hálózati rétegen való használatának kérése. A directory struktúra kinézete A directory rekordokra (továbbiakban: Entry) igazak a következő kijelentések: • Egy Entry az attribútumok egy halmazából áll. Ezek a faszerkezet csomópontjai • Minden attribútum rendelkezik névvel és egy vagy több ehhez rendelt értékkel. Ezen
attribútumok pontosan definiáltak, amit a címtár sémája (vagy sémái) rögzítenek • Mindegyik Entry rendelkezik egy egyedi azonosítóval, ami a már említett DN. A DN része a Relative Distinguished Name (RDN ), ugyanis hozzá a directory faszerkezetének egy útvonalán (PATH) tudunk eljutni. Ez az útvonal a csomópont rekordok sorozata, aminek a végén – mint levél – az RDN áll. Általában egy Entry mozgatható a fában és rendelkeznek egy UUID azonosítóval is, amit ettől teljesen független, de végső soron ez a bejegyzés teljes élettartama alatt biztosít az Entry számára egy egyedi azonosítót. Egy Entry megadható az un LDIF 6 text formátumban is, ami így néz ki: 1 2 3 4 5 6 7 8 9 10 11 12 dn : cn=John Doe , dc=example , dc=com cn : John Doe givenName : John sn : Doe telephoneNumber : +1 888 555 6789 telephoneNumber : +1 888 555 1232 m a i l : john@example . com manager : cn=Barbara Doe , dc=example , dc=com o b j e c t C l a s s : inetOrgPerson
objectClass : organizationalPerson objectClass : person o b j e c t C l a s s : top A fenti adatszerkezet csak példa, mert a bejegyzés típusától függően más és más attribútumok írhatják le a rekordot. A példában a dn: sor az Entry objektum megkülönböztető neve Ez nem része természetesen az Entry adatainak, célja csak az, hogy megadja azt a path-t, ahogy az objektumhoz el lehet jutni a címtár fában. A cn: az Entry RDN neve (cn=common name) A 6 LDIF=LDAP Data Interchange Format 105 Java programozói könyvtár Az Active Directory elérése dc=example,dc=com pedig az objektumunk szülő Entry-je, ahol a dc jelentése Domain Component. Ez egy tároló is egyben, hiszen az ilyen típusú objektumok halmaza közelíthető meg rajta keresztül. Láthatjuk azt is, hogy az attribútum nevek valamilyen emlékeztető nevek, esetünkben néhány példa: • givenName keresztnév • sn vezetéknév (surname) Egy LDAP szerver mindig egy címtár részfát is
jelent, a fenti esetben ez a dc=example,dc=com részfa. Ez egy kezdőpontja minden további bejegyzésnek Egy Entry lehet referencia egy másik szerverre is, például az ou (organisational unit=szervezeti egység) Entry által jelképezett részfa lehet egy másik szerveren is egy ilyen név esetén: ou=hr,dc=example,dc=com. Műveletek a címtárban Ebben a pontban áttekintjük a címtáron végezhető LDAP műveleteket. A legtöbb program igénye a Search and Compare műveletekre korlátozódik, mert nem akarják a címtár tartalmát megváltoztatni. Itt meg kell tanulnunk néhány alapfogalmat, amit az LDAP kliens programok lépten-nyomon használnak. 9.1 Meghatározás (baseObject) A bázis objektum (Entry) neve, ami akár a root (azaz a fa gyökére) is lehet. A keresés innen fog kezdődni a fában, amiatt base DN néven is szoktuk emlegetni 9.2 Meghatározás (scope) Azt határozza meg, hogy a baseObject alatt elindított keresés milyen körben történjen Ez lehet maga a
baseObject A másik eset a singleLevel, amikor közvetlenül a baseObject alatt keres, míg a legáltalánosabb lehetőség a wholeSubtree, ugyanis ekkor a teljes részfa átvizsgálásra kerül. 9.3 Meghatározás (filter) A filter egy keresési feltétel, ami a megadott scope-on belüli objektumokra kerül leellenőrzésre Tekintsünk egy példát: (&( o b j e c t C l a s s=p e r s o n ) ( | ( givenName=Imre ) ( m a i l= i n y i r i ∗ ) ) ) Ez a szűrő csak azon objektumokat fogja visszaadni, amik person típusúak, azaz személyek. A filter egy & jellel kezdődik, ez utal arra, hogy itt több szempont ÉS kapcsolata lesz. Ez volt az első, nézzük a másodikat. A pipe ( |) jel a VAGY kapcsolat művelete, azaz az ÉS második operandusa egy VAGY szerkezet: • A givenName Imre VAGY • a mail jellemző inyiri*-ra illeszkedik. Ezenfelül létezik még a tagadás, aminek „!” a jele. Fontos, hogy a filter kifejezés alapesetben kisbetű-nagybetű érzékeny. Egy
szűrőben használhatjuk a =, <= és >= relációs jeleket is 106 Java programozói könyvtár Az Active Directory elérése Amennyiben valakit részletesebben is érdekel az LDAP szűrő kifejezések, úgy ajánljuk ezt a helyet tanulmányozás céljából: http://www.ldapguruinfo/ldap/mastering-ldap-search-filters html . A derefAliases kereső kulcsszó a referenciák követésével kapcsolatos Ez az az eset, amikor az egyik Entry objektum hivatkozik a másikra. A keresés attributes kulcsszó azokat a jellemzőket adja meg, amiket vissza kell adni az eredmény objektumokból. A sizeLimit és timeLimit a keresés azon feltételeit specifikálja, hogy maximum mennyi darab Entry objektum érkezhet vissza, illetve a keresés meddig folytatható. A typesOnly megadása esetén csak a jellemzők típusa adódik vissza, értéke nem. A továbbiakban nézzük meg az egyéb LDAP műveleteket röviden: • Add : Az alábbi LDIF formátumú objektumot akarjuk beszúrni: 1 2 3 4 5 6
7 8 dn : u i d=u s e r , ou=p e o p l e , dc=example , dc=com c h a n g e t y p e : add o b j e c t C l a s s : top objectClass : person uid : user sn : l a s t −name cn : common−name u s e r P a s s w o r d : password Ekkor a beszúrandó uid=user,ou=people,dc=example,dc=com objektum nem létezhet, de a szülő ou=people,dc=example,dc=com objektumnak léteznie kell. • Bind : Az LDAP címtárhoz való kapcsolódás, aminek a hitelesítés (authentication) is része. Tekintettel arra, hogy egy címtárban minden objektumnak egy egyedi DN neve van, ezért az lesz a hagyományos értelemben vett username, ami mellett egy jelszót is meg kell adnunk. Hitelesítés után a létrejött session bizonyos attribútumokat is tartalmaz, ezért is nevezzük ezt a folyamatot bind-nak. • Delete: Kitöröl egy objektumot. • Modify: Egy objektum módosítása. • Modify DN : Egy objektum mozgatása vagy átnevezése. • Abandon: Az éppen futó művelet megszakítása. • Unbind :
Lekapcsolódás az LDAP szerverről. Az LDAP URL általános kinézete Az LDAP URL általános alakja a következő, de ezek egy része opcionális: l d a p : // h o s t : p o r t /DN? a t t r i b u t e s ? s c o p e ? f i l t e r ? e x t e n s i o n s Az URL egyes részeinek jelentése a következő: • host:port: Az LDAP szerviz itt érhető el. • DN : a DN név 107 Java programozói könyvtár Az Active Directory elérése • attributes: vesszővel elválasztva itt adhatjuk meg, hogy a válaszban mely attribútumokat szeretnénk visszakapni. • scope: A már ismertetett scope adható itt meg. • filter : Az bemutatott filter kifejezés helye. • extensions: Az LDAP kiterjesztések megadási helye. Az LDAP Java API A javax.namingdirectory csomag tartalmazza azokat az osztályokat és interface-eket, amik egy LDAP szerverrel (így az AD is) való együttműködéshez szükséges. Létezik még sok olyan külső jar is, ami ugyanezt a feladatot látja el, de azokat csak
szükség esetén érdemes használni. Szeretnénk felhívni a figyelmet az Apache Directory Projectre (webhelye: http://directory.apacheorg/), aminek 3 fontos szoftver eleme van: • ApacheDS : Egy teljes értékű címtár szerver. • Apache Directory Studio: Egy Eclipse alapú LDAP kliens. • Apache Directory LDAP API : Egy Java könyvtár, ami implementálja az LDAP API-t. Ez is használható lenne a JDK beépített API-ja helyett. Első példa egyszerű lekérdezésre Az alábbiakban látható példánk forráskódja: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import import import import java . u t i l Hashtable ; j a v a x . naming l d a p ∗ ; j a v a x . naming d i r e c t o r y ∗ ; j a v a x . naming ∗ ; public c l a s s ADTest { public s t a t i c void main ( S t r i n g [ ] a r g s ) { H a s h t a b l e env = new H a s h t a b l e ( ) ; S t r i n g userName = "CN=i n y i r i ,CN=CegUsers ,DC=ceg ,DC=com" ; S t r i n g u
s e r P a s s w o r d = "XXXXXXX" ; S t r i n g ldapURL = " l d a p : / / mydc . c e g com : 3 8 9 " ; env . put ( Context INITIAL CONTEXT FACTORY, "com sun j n d i l d a p LdapCtxFactory " ) ; // s e t s e c u r i t y c r e d e n t i a l s env . put ( Context SECURITY AUTHENTICATION, " s i m p l e " ) ; env . put ( Context SECURITY PRINCIPAL, userName ) ; env . put ( Context SECURITY CREDENTIALS, adminPassword ) ; // s p e c i f y u s e o f s s l env . put ( Context SECURITY PROTOCOL, " s s l " ) ; 108 Java programozói könyvtár 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 Az Active Directory elérése // c o n n e c t t o my domain c o n t r o l l e r env . put ( Context PROVIDER URL, ldapURL ) ; try { // C r e a t e t h e i n i t i a l d i r e c t o r y c o n t e x t Di rC on t ex t c
t x = new I n i t i a l L d a p C o n t e x t ( env , n u l l ) ; // C r e a t e t h e s e a r c h c o n t r o l s S e a r c h C o n t r o l s s e a r c h C t l s = new S e a r c h C o n t r o l s ( ) ; // S p e c i f y t h e a t t r i b u t e s t o r e t u r n S t r i n g r e t u r n e d A t t s [ ] = { " sn " , " m a i l " , " cn " , " te le ph o ne nu mb er " } ; searchCtls . setReturningAttributes ( returnedAtts ) ; // S p e c i f y t h e s e a r c h s c o p e s e a r c h C t l s . s e t S e a r c h S c o p e ( S e a r c h C o n t r o l s SUBTREE SCOPE) ; // s p e c i f y t h e LDAP s e a r c h f i l t e r S t r i n g s e a r c h F i l t e r = " (&( o b j e c t C l a s s=u s e r ) ( m a i l =∗) ) " ; // S p e c i f y t h e Base f o r t h e s e a r c h S t r i n g s e a r c h B a s e = "DC=ceg ,DC=com" ; // i n i t i a l i z e c o u n t e r t o t o t a l t h e r e s u l t s int t o t a l R e s u l t s = 0 ; // Search
f o r o b j e c t s u s i n g t h e f i l t e r NamingEnumeration answer = c t x . s e a r c h ( s e a r c h B a s e , s e a r c h F i l t e r , s e a r c h C t l s ) ; // Loop t h r o u g h t h e s e a r c h r e s u l t s while ( answer . hasMoreElements ( ) ) { S e a r c h R e s u l t s r = ( S e a r c h R e s u l t ) answer . n e x t ( ) ; t o t a l R e s u l t s ++; System . out p r i n t l n ( ">>>" + s r getName ( ) ) ; } // P r i n t o u t some o f t h e a t t r i b u t e s , c a t c h t h e e x c e p t i o n i f t h e a t t r i b u t e s have no v a l u e s Attributes attrs = sr . getAttributes () ; i f ( a t t r s != n u l l ) { try { System . out p r i n t l n ( " ␣ ␣ ␣ surname : ␣ " + a t t r s g e t ( " sn " ) g e t ( ) ) ; System . out p r i n t l n ( " ␣ ␣ ␣ f i r s t n a m e : ␣ " + a t t r s g e t ( " givenName " ) g e t ( ) ) ; System . out p r i n t l n ( " ␣ ␣ ␣ m a i l :
␣ " + a t t r s g e t ( " m a i l " ) g e t ( ) ) ; } catch ( N u l l P o i n t e r E x c e p t i o n e ) { System . out p r i n t l n ( " E r r o r s ␣ l i s t i n g ␣ a t t r i b u t e s : ␣ " + e ) ; } } System . out p r i n t l n ( " T o t a l ␣ r e s u l t s : ␣ " + t o t a l R e s u l t s ) ; ctx . c l o s e () ; } } } catch ( NamingException e ) { System . e r r p r i n t l n ( " Problem ␣ s e a r c h i n g ␣ d i r e c t o r y : ␣ " + e ) ; } Tekintsük át a fontosabb részek jelentését! A 14-16 sorokban egy-egy stringbe mentjük a bejelentkezéshez szükséges felhasználói nevet, jelszót és az LDAP connection stringet. Az env egy gyűjteménye azoknak a paramétereknek, amik a BIND igényel, azaz a kapcsolódáshoz szükséges. 109 Java programozói könyvtár Az Active Directory elérése Mindez a 18-29 sorok között van megadva, a program megjegyzéseiből kiolvashatóak a szerepek. A 34.
sor ctx változója egy kontextust kér és tárol az LDAP szerver felé A 40-41 sorok returnedAtts tömbje és annak beállítása fogja majd specifikálni, hogy mely jellemzőkre van szükségünk a keresés eredményeképpen kapott válaszban. A 44 sorban egy keresési vezérlési szabályt deklarálunk, azaz azt kérjük, hogy majd a base DN alatti teljes részfában történjen a keresés. A 47 sor egy kereső filter kifejezés, ami egy ÉS kapcsolatban azt mondja, hogy kérünk minden user típusú objektumot, ahol a mail bármi. Az 50 sor szöveges változója pedig a keresés base DN-jét tárolja Az 56. sorban lefuttatjuk magát a keresést, aminek a válasza az answer változó által referált NamingEnumeration típusú objektumba kerül. Az 59-78 sorok közötti ciklus a választ dolgozza fel, azaz jelen esetben kiírja a képernyőre a kapott értékeket. Végiglépdelünk az iteráción, ahol az egyes iterálandó elemek SearchResult típusúak. Az értékek lekérése
egyszerűen megérthető a kód 64-72 sorok közötti részéből. Befejezésként fontos lépés a 81 sor lezáró művelete Az LDAPManager példakód A most következő példakód Brett McLaughlintől származik, a Java LDAP API sok vonatkozását megismerhetjük belőle. A kódot teljes egészében közöljük, de kiemeljük, hogy ez a wwworeillycom terméke és szabadon felhasználható. Az egyes alpontokban metódusonként mutatjuk be az LDAPManager lehetőségeit A kód elején konstansokat adtunk meg, amik a következő dolgokat specifikálják: • A userek tárolása melyik DN alatt lesz (USERS OU ) • A csoportok tárolása melyik DN alatt lesz (GROUPS OU ) • A jogok tárolásának base objektuma (PERMISSIONS OU ) • Az LDAP szerver alapértelmezett portja (DEFAULT PORT ) A getInstance() gyártó és néhány kisegítő metódus Az LDAPManager alapstruktúráját – aminek további részleteit a későbbi pontok tárgyalják – a következő kód mutatja: 1 2 3 4 5 6 7
8 9 10 11 12 13 14 15 16 17 package t e s t ; import import import import import import import import import import import import import import import 110 j a v a . u t i l HashMap ; java . u t i l Properties ; java . u t i l LinkedList ; java . u t i l List ; j a v a . u t i l Map ; j a v a x . naming Context ; j a v a x . naming NameNotFoundException ; j a v a x . naming NamingEnumeration ; j a v a x . naming NamingException ; j a v a x . naming d i r e c t o r y A t t r i b u t e ; j a v a x . naming d i r e c t o r y A t t r i b u t e I n U s e E x c e p t i o n ; j a v a x . naming d i r e c t o r y A t t r i b u t e s ; j a v a x . naming d i r e c t o r y B a s i c A t t r i b u t e ; j a v a x . naming d i r e c t o r y B a s i c A t t r i b u t e s ; j a v a x . naming d i r e c t o r y D ir Co n te xt ; Java programozói könyvtár 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 import import import import import j a v a x . naming j a v a x . naming j a v a x . naming j a v a x . naming j a v a x . naming directory directory directory directory directory Az Active Directory elérése . InitialDirContext ; . ModificationItem ; . NoSuchAttributeException ; . SearchControls ; . SearchResult ; public c l a s s LDAPManager { /∗ ∗ The OU ( o r g a n i z a t i o n a l u n i t ) t o add u s e r s t o ∗/ private s t a t i c f i n a l S t r i n g USERS OU = " ou=People , o=f o r e t h o u g h t . com" ; /∗ ∗ The OU ( o r g a n i z a t i o n a l u n i t ) t o add g r o u p s t o ∗/ private s t a t i c f i n a l S t r i n g GROUPS OU = " ou=Groups , o=f o r e t h o u g h t . com" ; /∗ ∗ The OU ( o r g a n i z a t i o n a l u n i t ) t o add p e r m i s s i o n s t o ∗/ private s t a t i c f i n a l S t r i n g PERMISSIONS OU = " ou=P e r m i
s s i o n s , o=f o r e t h o u g h t . com" ; /∗ ∗ The d e f a u l t LDAP p o r t ∗/ private s t a t i c f i n a l i n t DEFAULT PORT = 3 8 9 ; /∗ ∗ The private /∗ ∗ The private LDAPManager i n s t a n c e o b j e c t ∗/ s t a t i c Map i n s t a n c e s = new HashMap ( ) ; c o n n e c t i o n , t h r o u g h a <code>DirContext </code >, t o LDAP ∗/ D ir Co nt e xt c o n t e x t ; /∗ ∗ The hostname c o n n e c t e d t o ∗/ private S t r i n g hostname ; /∗ ∗ The p o r t c o n n e c t e d t o ∗/ private i n t p o r t ; protected LDAPManager ( S t r i n g hostname , i n t p o r t , S t r i n g username , S t r i n g password ) throws NamingException { c o n t e x t = g e t I n i t i a l C o n t e x t ( hostname , p o r t , username , password ) ; } // Only s a v e d a t a i f we g o t c o n n e c t e d t h i s . hostname = hostname ; this . port = port ; public s t a t i c LDAPManager g e t I n s t a n c e ( S t r i n g hostname , int
port , S t r i n g username , S t r i n g password ) throws NamingException { // C o n s t r u c t t h e k ey f o r t h e s u p p l i e d i n f o r m a t i o n S t r i n g key = new S t r i n g B u f f e r ( ) . append ( hostname ) . append ( " : " ) . append ( p o r t ) . append ( " | " ) . append ( ( username == n u l l ? " " : username ) ) . append ( " | " ) . append ( ( password == n u l l ? " " : password ) ) . toString () ; if ( ! i n s t a n c e s . c o n t a i n s K e y ( key ) ) { synchronized ( LDAPManager . c l a s s ) { 111 Java programozói könyvtár 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 if } } } } ( ! i n s t a n c e s . c o n t a i n s K e y ( key ) ) { LDAPManager i n s t a n c e = new LDAPManager (
hostname , p o r t , username , password ) ; i n s t a n c e s . put ( key , i n s t a n c e ) ; return i n s t a n c e ; return ( LDAPManager ) i n s t a n c e s . g e t ( key ) ; public s t a t i c LDAPManager g e t I n s t a n c e ( S t r i n g hostname , i n t p o r t ) throws NamingException { } return g e t I n s t a n c e ( hostname , por t , null , n u l l ) ; public s t a t i c LDAPManager g e t I n s t a n c e ( S t r i n g hostname ) throws NamingException { . } return g e t I n s t a n c e ( hostname , DEFAULT PORT, null , n u l l ) ; private S t r i n g getUserDN ( S t r i n g username ) { return new S t r i n g B u f f e r ( ) . append ( " u i d=" ) . append ( username ) . append ( " , " ) . append (USERS OU) . toString () ; } private S t r i n g getUserUID ( S t r i n g userDN ) { i n t s t a r t = userDN . in dex Of ( "=" ) ; i n t end = userDN . ind exO f ( " , " ) ; i f ( end == −1) { end = userDN . l e n g t h ( ) ; }
} return userDN . s u b s t r i n g ( s t a r t +1 , end ) ; private S t r i n g getGroupDN ( S t r i n g name ) { return new S t r i n g B u f f e r ( ) . append ( " cn=" ) . append ( name ) . append ( " , " ) . append (GROUPS OU) . toString () ; } private S t r i n g getGroupCN ( S t r i n g groupDN ) { i n t s t a r t = groupDN . i nde xOf ( "=" ) ; i n t end = groupDN . in dex Of ( " , " ) ; i f ( end == −1) { end = groupDN . l e n g t h ( ) ; } } 112 return groupDN . s u b s t r i n g ( s t a r t +1 , end ) ; Az Active Directory elérése Java programozói könyvtár 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 Az Active Directory elérése private S t r i n g getPermissionDN ( S t r i n g name ) { return new S t r i n g B u f f e r ( ) . append ( " cn=" ) . append ( name ) .
append ( " , " ) . append (PERMISSIONS OU) . toString () ; } private S t r i n g getPermissionCN ( S t r i n g permissionDN ) { i n t s t a r t = permissionDN . ind exO f ( "=" ) ; i n t end = permissionDN . in de xOf ( " , " ) ; i f ( end == −1) { end = permissionDN . l e n g t h ( ) ; } } return permissionDN . s u b s t r i n g ( s t a r t +1 , end ) ; private D ir Co nt e xt g e t I n i t i a l C o n t e x t ( S t r i n g hostname , i n t p o r t , S t r i n g username , S t r i n g password ) throws NamingException { S t r i n g providerURL = new S t r i n g B u f f e r ( " l d a p : / / " ) . append ( hostname ) . append ( " : " ) . append ( p o r t ) . toString () ; P r o p e r t i e s p r o p s = new P r o p e r t i e s ( ) ; p r o p s . put ( Context INITIAL CONTEXT FACTORY, "com . sun j n d i l d a p LdapCtxFactory " ) ; p r o p s . put ( Context PROVIDER URL, providerURL ) ; i f ( ( username != n u l l )
&& ( ! username . e q u a l s ( " " ) ) ) { p r o p s . put ( Context SECURITY AUTHENTICATION, " s i m p l e " ) ; p r o p s . put ( Context SECURITY PRINCIPAL, username ) ; p r o p s . put ( Context SECURITY CREDENTIALS, ( ( password == n u l l ) ? " " : password ) ) ; } } return new I n i t i a l D i r C o n t e x t ( p r o p s ) ; . } // end c l a s s Az első, amit talán észreveszünk, hogy az LDAPManager konstruktora protected, emögött az a cél húzódik meg, ebből egy példányt valamelyik getInstance() gyártómetódussal hozzunk létre, amik persze belül ezzel hoznak létre új példányt. A getInstance() legteljesebb paraméterezésű változata ezeket az argumentumokat képes átvenni: • hostname, port • username, password A 70-78 sorok között egy string kerül összeállításra erről a 4 darab paraméterről. Az instances egy HashMap, amit még a 42 sorban hoztunk létre Kliensünk képes több LDAP szerver session-t
egyszerre fenntartani, de ha már van ilyen, akkor azt használja. Emiatt kellett az előző 113 Java programozói könyvtár Az Active Directory elérése string is, ugyanis ez azonosítja ezeket a kapcsolatokat a Map-ben. Ennek megfelelően működik a 81-91 sorok közötti kód Amennyiben nincs a megadott paraméterekre illeszkedő session az instances tárolóban, úgy azt a konstruktorral létrehozza, majd beteszi oda. A végeredmény mindig az lesz, hogy egy LDAPManager példányt kapunk. A használt konstruktor fő feladata a context adattag inicializálása (azaz a BIND művelet), ami az 56. sorban történik meg Itt érdemes gyorsan átnézni a privát metódusokat is, mert azokat a későbbiekben majd használni fogjuk. Az első és legfontosabb ilyen a getInitialContext(), ugyanis itt van megvalósítva a konstruktor lényegi része A metódus egy DirContext objektumot ad vissza A 172-177 sorokban felépítjük az LDAP URL (ldap://.) stringet, amit a providerURL
változóhoz rendeltünk Az összes kapcsolódáshoz szükséges paraméter egy props nevű Properties változóba kerül, amivel aztán előállítjuk a visszaadandó InitialDirContext objektumot. Mindezzel azt értük el, hogy van egy új session az LDAP (vagy AD) szerver felé. A getUserDN() metódus feladata az, hogy egy uid=user ,ou=People,o=forethought.com alakú stringet szolgáltasson, ahol a user egy bemenő paraméter Ez a felhasználói DN A getGroupDN() szerepe hasonló, neki egy csoport neve alapján a cn=group,ou=Groups,o=forethought.com alakú szöveget kell szolgáltatnia A getUserUID() a fenti uid -del kezdődő DN névből kiveszi az első „=” és „ ,” jelek közötti részt, azaz a user nevét. A getGroupCN() hasonlóan ugyanezt teszi, de a csoport cn, azaz RDN nevét adja vissza. A getPermissionDN() egy cn=permission,ou=Permissions,o=forethoughtcom alakú stringet gyárt le, ami egy-egy jog DN neve. A getPermissionCN() pedig ennek – hasonlóan a fentiekhez
– a jog RDN nevét vágja ki. Az addUser() metódus A korábbiakban már említettük, hogy az LDAP címtár egy séma alapján írja le a saját adatszerkezetét, amiben az Entry objektumok is ott vannak. Ez általában úgy van megvalósítva, hogy több sémafájl együtt írja le a teljes sémát, amit utána a szerver indulásakor ismer és használ. Itt van például a core.schema fájl egy kicsi részlete: 1 2 3 4 5 6 // c o r e . schema . o b j e c t c l a s s ( 2 . 5 6 6 NAME ’ p e r s o n ’ SUP top STRUCTURAL MUST ( sn $ cn ) MAY ( u s e r P a s s w o r d $ telephoneNumber $ s e e A l s o $ d e s c r i p t i o n ) ) . Láthatjuk, hogy itt most a person típust megadó sémarészletet látjuk. Már itt az elején érdemes észrevenni, hogy a megadott attribútumok egy része kötelező (MUST), másik része pedig csak lehetőség (MAY). Ennyi előzmény után már jobban érthető lesz az addUser() metódus működése, aminek az a feladata, hogy egy új DN Entry-t,
pontosabban egy felhasználót szúrjon be a címtárba. Tekintettel arra, hogy 4 darab paraméterünk van, ezek lesznek majd a bejegyzésben szereplő jellemzők. A 9 sorban egy container változót készítünk a jellemzők tárolására, ez standard módon az Attributes típus lesz. Az objClasses egy másik ugyanilyen konténer, de ennek kifejezett célja az lesz, hogy a sémának megfelelő attribútumok majd benne legyenek (12. sor) A 1316 sorok között alakítjuk ki a végső sémát a top, person, organizationalPerson és inetOrgPerson séma osztályok alapján. Mindegyik hozzáadja a saját maga által leírt mezőket, a person-t például bemutattuk. A 18-22 sorok között csak egy olyan string került előállításra, ami a személy teljes 114 Java programozói könyvtár Az Active Directory elérése nevét tartalmazza. A 23-30 sorok között létrehozzuk a feltöltendő értékeket reprezentáló 5 darab Attribute objektumot. A 33-38 sorok között felépítjük a
container objektumot, előbb a tárolót, aztán hozzáadjuk a jellemzőket. A 41 sor az így előkészített container tartalmát elhelyezi a címtárba oda, amit a már ismert getUserDN() metódus kijelöl számára. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 . public c l a s s LDAPManager { . public void addUser ( S t r i n g username , S t r i n g f i r s t N a m e , S t r i n g lastName , S t r i n g password ) throws NamingException { // C r e a t e a c o n t a i n e r s e t o f a t t r i b u t e s A t t r i b u t e s c o n t a i n e r = new B a s i c A t t r i b u t e s ( ) ; // C r e a t e t h e o b j e c t c l a s s t o add A t t r i b u t e o b j C l a s s e s = new B a s i c A t t r i b u t e ( " o b j e c t C l a s s " ) ; o b j C l a s s e s . add ( " top " ) ; o b j C l a s s e s . add ( " p e r s o n " ) ; o b j C l a s s e s . add ( " o r g a n i z a t i o
n a l P e r s o n " ) ; o b j C l a s s e s . add ( " i n e t O r g P e r s o n " ) ; // A s s i g n t h e username , f i r s t name , and l a s t name S t r i n g cnValue = new S t r i n g B u f f e r ( f i r s t N a m e ) . append ( " ␣ " ) . append ( lastName ) . toString () ; A t t r i b u t e cn = new B a s i c A t t r i b u t e ( " cn " , cnValue ) ; A t t r i b u t e givenName = new B a s i c A t t r i b u t e ( " givenName " , f i r s t N a m e ) ; A t t r i b u t e sn = new B a s i c A t t r i b u t e ( " sn " , lastName ) ; A t t r i b u t e u i d = new B a s i c A t t r i b u t e ( " u i d " , username ) ; // Add password Attribute userPassword = new B a s i c A t t r i b u t e ( " u s e r p a s s w o r d " , password ) ; // Add t h e s e t o t h e c o n t a i n e r c o n t a i n e r . put ( o b j C l a s s e s ) ; c o n t a i n e r . put ( cn ) ; c o n t a i n e r . put ( sn ) ; c o n t a i n e r .
put ( givenName ) ; c o n t a i n e r . put ( u i d ) ; c o n t a i n e r . put ( u s e r P a s s w o r d ) ; // C r e a t e t h e e n t r y c o n t e x t . c r e a t e S u b c o n t e x t ( getUserDN ( username ) , c o n t a i n e r ) ; } . } // end c l a s s A deleteUser() metódus Ezen metódus feladata az, hogy kitörölje a paraméterben megadott felhasználót a címtárból, aminek DN nevét szintén a getUserDN() segítségével állítja elő. Maga a megvalósítás nem túl nehéz, mindössze egyetlen sor, mert semmi újat nem kell előállítani, a megoldás mindössze a context objektumra meghívott destroySubcontext() metódus, ami értelemszerűen a törlendő DN-t kapja. 1 2 3 . public c l a s s LDAPManager { . 115 Java programozói könyvtár 4 5 6 7 8 9 10 11 12 Az Active Directory elérése public void d e l e t e U s e r ( S t r i n g username ) throws NamingException { try { c o n t e x t . d e s t r o y S u b c o n t e x t ( getUserDN ( username ) ) ; }
catch ( NameNotFoundException e ) { // I f t h e u s e r i s n o t found , i g n o r e t h e e r r o r } } . } // end c l a s s Az isValidUser() metódus Ezzel a logikai metódussal le tudjuk azt ellenőrizni, hogy egy megadott user/password birtokában be tudunk-e lépni a címtárba. Ehhez mindössze annyit ellenőriz le, hogy a context objektumot meg tudjuk-e szerezni. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 . public c l a s s LDAPManager { . public boolean i s V a l i d U s e r ( S t r i n g username , S t r i n g password ) throws NameNotFoundException { try { Di rC o nt ex t c o n t e x t = g e t I n i t i a l C o n t e x t ( hostname , p o r t , getUserDN ( username ) , password ) ; return true ; } catch ( j a v a x . naming NameNotFoundException e ) { throw new NameNotFoundException ( username ) ; } catch ( NamingException e ) { // Any o t h e r e r r o r i n d i c a t e s c o u l d n ’ t l o g u s e r i n return f a l s e ; } } . } // end c l a s s Az addGroup()
metódus Az addGroup() egy új csoportot helyez el a getGroupDN() által visszaadott helyre. Működése az addUser()-hez hasonló, de a használt objClass-ok egy része természetesen más, lévén csoport leírásról van szó. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 . public c l a s s LDAPManager { . public void addGroup ( S t r i n g name , S t r i n g d e s c r i p t i o n ) throws NamingException { // C r e a t e a c o n t a i n e r s e t o f a t t r i b u t e s A t t r i b u t e s c o n t a i n e r = new B a s i c A t t r i b u t e s ( ) ; // C r e a t e t h e o b j e c t c l a s s t o add A t t r i b u t e o b j C l a s s e s = new B a s i c A t t r i b u t e ( " o b j e c t C l a s s " ) ; o b j C l a s s e s . add ( " top " ) ; o b j C l a s s e s . add ( " groupOfUniqueNames " ) ; o b j C l a s s e s . add ( " groupOfForethoughtNames " ) ; // A s s i g n t h e name and d e s c r i p t i o n t o t h e group 116 Java programozói
könyvtár 17 18 19 20 21 22 23 24 25 26 27 28 29 Az Active Directory elérése A t t r i b u t e cn = new B a s i c A t t r i b u t e ( " cn " , name ) ; A t t r i b u t e d e s c = new B a s i c A t t r i b u t e ( " d e s c r i p t i o n " , d e s c r i p t i o n ) ; // Add t h e s e t o t h e c o n t a i n e r c o n t a i n e r . put ( o b j C l a s s e s ) ; c o n t a i n e r . put ( cn ) ; c o n t a i n e r . put ( d e s c ) ; // C r e a t e t h e e n t r y c o n t e x t . c r e a t e S u b c o n t e x t ( getGroupDN ( name ) , c o n t a i n e r ) ; } . } // end c l a s s A deleteGroup() metódus A csoport törlését végző deleteGroup() logikája teljesen hasonló, mint a deleteUser() volt, de a törlés szülőhelyét természetszerűleg a getGroupDN() által visszaadott kontextus string adja meg. 1 2 3 4 5 6 7 8 9 10 11 12 . public c l a s s LDAPManager { . public void d e l e t e G r o u p ( S t r i n g name ) throws NamingException { try { c o n t
e x t . d e s t r o y S u b c o n t e x t ( getGroupDN ( name ) ) ; } catch ( NameNotFoundException e ) { // I f t h e group i s n o t found , i g n o r e t h e e r r o r } } . } // end c l a s s Az addPermission() metódus Egy jogelem hozzáadási logikája sem különbözik a többitől, de itt az erre kitalált forethoughtPermission sémarész objektumot is használjuk, illetve a beszúrás helyét a getPermissionDN() kalkulálja ki nekünk. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 . public c l a s s LDAPManager { . public void a d d P e r m i s s i o n ( S t r i n g name , S t r i n g d e s c r i p t i o n ) throws NamingException { // C r e a t e a c o n t a i n e r s e t o f a t t r i b u t e s A t t r i b u t e s c o n t a i n e r = new B a s i c A t t r i b u t e s ( ) ; // C r e a t e t h e o b j e c t c l a s s t o add A t t r i b u t e o b j C l a s s e s = new B a s i c A t t r i b u t e ( " o b j e c t C l a s s " ) ; o b j C l a s s e s . add (
" top " ) ; o b j C l a s s e s . add ( " f o r e t h o u g h t P e r m i s s i o n " ) ; // A s s i g n t h e name and d e s c r i p t i o n t o t h e group A t t r i b u t e cn = new B a s i c A t t r i b u t e ( " cn " , name ) ; A t t r i b u t e d e s c = new B a s i c A t t r i b u t e ( " d e s c r i p t i o n " , d e s c r i p t i o n ) ; // Add t h e s e t o t h e c o n t a i n e r c o n t a i n e r . put ( o b j C l a s s e s ) ; c o n t a i n e r . put ( cn ) ; 117 Java programozói könyvtár 22 23 24 25 26 27 28 Az Active Directory elérése c o n t a i n e r . put ( d e s c ) ; // C r e a t e t h e e n t r y c o n t e x t . c r e a t e S u b c o n t e x t ( getPermissionDN ( name ) , c o n t a i n e r ) ; } . } // end c l a s s A deletePermission() metódus A deletePermission() a már ismer módszer szerint töröl egy jogelemet. 1 2 3 4 5 6 7 8 9 10 11 12 . public c l a s s LDAPManager { . public void d e l e t e P e r m
i s s i o n ( S t r i n g name ) throws NamingException { try { c o n t e x t . d e s t r o y S u b c o n t e x t ( getPermissionDN ( name ) ) ; } catch ( NameNotFoundException e ) { // I f t h e p e r m i s s i o n i s n o t found , i g n o r e t h e e r r o r } } . } // end c l a s s Az assignUser() metódus Az assignUser() egy felhasználót betesz a specifikált csoportba. Ehhez egy 1 elemű ModificationItem tömb szükséges A 10-12 sorokban egy mod attribútumot hozunk létre, ahol a uniqueMember jellemzőhöz a felhasználónk DN-jét rendeljük hozzá. A 13-14 sorok ezt azt 1 módosítási művelet kérést helyezik el a mods tömbbe, majd a 15. sorban végrehajtatjuk a kérést a getGroupDN() helyen lévő csoportra. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 . public c l a s s LDAPManager { . public void a s s i g n U s e r ( S t r i n g username , S t r i n g groupName ) throws NamingException { try { M o d i f i c a t i o n I t e m [ ] mods = new M o d i f i c
a t i o n I t e m [ 1 ] ; A t t r i b u t e mod = new B a s i c A t t r i b u t e ( " uniqueMember " , getUserDN ( username ) ) ; mods [ 0 ] = new M o d i f i c a t i o n I t e m ( Di r C o n t e x t .ADD ATTRIBUTE, mod) ; c o n t e x t . m o d i f y A t t r i b u t e s ( getGroupDN ( groupName ) , mods ) ; } catch ( A t t r i b u t e I n U s e E x c e p t i o n e ) { // I f u s e r i s a l r e a d y added , i g n o r e e x c e p t i o n } } . } // end c l a s s 118 Java programozói könyvtár Az Active Directory elérése A removeUser() metódus A removeUser() az előző művelet fordítottja, azaz eltávolítja a felhasználót a csoportból. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 . public c l a s s LDAPManager { . public void removeUser ( S t r i n g username , S t r i n g groupName ) throws NamingException { try { M o d i f i c a t i o n I t e m [ ] mods = new M o d i f i c a t i o n I t e m [ 1 ] ; A t t r i b u t e mod = new B a s i c A t t r i
b u t e ( " uniqueMember " , getUserDN ( username ) ) ; mods [ 0 ] = new M o d i f i c a t i o n I t e m ( Di r C o n t e x t .REMOVE ATTRIBUTE, mod) ; c o n t e x t . m o d i f y A t t r i b u t e s ( getGroupDN ( groupName ) , mods ) ; } catch ( N o S u c h A t t r i b u t e E x c e p t i o n e ) { // I f u s e r i s n o t a s s i g n e d , i g n o r e t h e e r r o r } } . } // end c l a s s A userInGroup() metódus Ez a userInGroup() logikai metódus azért fontos, mert egy ilyen vizsgálat lényeges része az alkalmazás jogosultságkezelési rendszerének. A csoport részéről továbbra is a uniqueMember mező lesz a kulcs attribútum, amit a searchAttributes tömbben el is helyeztünk. A 11-13 sorokban a csoport DN és ezen tömb alapján megalkotjuk az attributes változót. A 15 sorban látható módon lekérhetjük a memberAtts által mutatott objektumot, amit végig tudunk járni és fel tudunk dolgozni, mint NamingEnumeration objektum. Amennyiben ezek között
van a mi user-ünk, úgy benne van a csoportban. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 . public c l a s s LDAPManager { . public boolean userInGroup ( S t r i n g username , S t r i n g groupName ) throws NamingException { // S e t up a t t r i b u t e s t o s e a r c h f o r S t r i n g [ ] s e a r c h A t t r i b u t e s = new S t r i n g [ 1 ] ; s e a r c h A t t r i b u t e s [ 0 ] = " uniqueMember " ; Attributes attributes = c o n t e x t . g e t A t t r i b u t e s ( getGroupDN ( groupName ) , searchAttributes ) ; i f ( a t t r i b u t e s != n u l l ) { A t t r i b u t e memberAtts = a t t r i b u t e s . g e t ( " uniqueMember " ) ; i f ( memberAtts != n u l l ) { f o r ( NamingEnumeration v a l s = memberAtts . g e t A l l ( ) ; v a l s . hasMoreElements ( ) ; ) { i f ( username . e q u a l s I g n o r e C a s e ( getUserUID ( ( S t r i n g ) v a l s . nextElement ( ) ) ) ) { return true ; } } 119 Java programozói könyvtár
25 26 27 28 29 30 31 } } Az Active Directory elérése } return f a l s e ; . } // end c l a s s A getMembers() metódus A getMembers() működése hasonlít a userInGroup()-hoz, de ahelyett, hogy vizsgálná a user tartalmazását, visszaadja a csoport összes felhasználóját. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 . public c l a s s LDAPManager { . public L i s t getMembers ( S t r i n g groupName ) throws NamingException { L i s t members = new L i n k e d L i s t ( ) ; // S e t up a t t r i b u t e s t o s e a r c h f o r S t r i n g [ ] s e a r c h A t t r i b u t e s = new S t r i n g [ 1 ] ; s e a r c h A t t r i b u t e s [ 0 ] = " uniqueMember " ; Attributes attributes = c o n t e x t . g e t A t t r i b u t e s ( getGroupDN ( groupName ) , searchAttributes ) ; i f ( a t t r i b u t e s != n u l l ) { A t t r i b u t e memberAtts = a t t r i b u t e s . g e t ( " uniqueMember " ) ; i f ( memberAtts != n u l l ) { f
o r ( NamingEnumeration v a l s = memberAtts . g e t A l l ( ) ; v a l s . hasMoreElements ( ) ; members . add ( getUserUID ( ( S t r i n g ) v a l s . nextElement ( ) ) ) ) ; } } } return members ; . } // end c l a s s A getGroups() metódus A getGroups() visszaadja a megadott user összes csoportjának a nevét. Ez lehetővé teszi például annak a technikának a megvalósítását, hogy hitelesítés után egy olyan User objektum kerüljön a session-re, ami már a csoportjainak listáját is tartalmazza. A megoldás kulcsa egy jól megfogalmazott filter, ami azt kéri, hogy csak azok az objektumok kerüljenek legyűjtésre, ahol az objectClass=groupOfForethoughtNames ÉS a uniqueMember értéke pedig a mi felhasználói nevünk. A 18-19 sorok mutatják, hogy most 1 szintű scope lesz használva, azaz nem kell keresnünk a részfában. Ennek az az oka, hogy a csoportok közvetlenül a GROUPS OU alatt vannak 1 2 3 4 5 6 . public c l a s s LDAPManager { . public L i s t getGroups
( S t r i n g username ) throws NamingException { L i s t g r o u p s = new L i n k e d L i s t ( ) ; 120 Java programozói könyvtár 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 Az Active Directory elérése // S e t up c r i t e r i a t o s e a r c h on S t r i n g f i l t e r = new S t r i n g B u f f e r ( ) . append ( "(&" ) . append ( " ( o b j e c t C l a s s=groupOfForethoughtNames ) " ) . append ( " ( uniqueMember=" ) . append ( getUserDN ( username ) ) . append ( " ) " ) . append ( " ) " ) . toString () ; // S e t up s e a r c h c o n s t r a i n t s S e a r c h C o n t r o l s c o n s = new S e a r c h C o n t r o l s ( ) ; c o n s . s e t S e a r c h S c o p e ( S e a r c h C o n t r o l s ONELEVEL SCOPE) ; NamingEnumeration r e s u l t s = c o n t e x t . s e a r c h (GROUPS OU, f i l t e r , cons ) ; while ( r e s u l t s . hasMore ( ) ) { SearchResult r e s u l t = (
SearchResult ) r e s u l t s . next ( ) ; g r o u p s . add ( getGroupCN ( r e s u l t getName ( ) ) ) ; } } return g r o u p s ; . } // end c l a s s Az assignPermission() metódus Az assignPermission() célja, hogy egy jogelemet egy csoporthoz rendeljen. A megvalósítás módja analóg a assignUser() metódussal. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 . public c l a s s LDAPManager { . public void a s s i g n P e r m i s s i o n ( S t r i n g groupName , S t r i n g permissionName ) throws NamingException { try { M o d i f i c a t i o n I t e m [ ] mods = new M o d i f i c a t i o n I t e m [ 1 ] ; A t t r i b u t e mod = new B a s i c A t t r i b u t e ( " u n i q u e P e r m i s s i o n " , getPermissionDN ( permissionName ) ) ; mods [ 0 ] = new M o d i f i c a t i o n I t e m ( Di r C o n t e x t .ADD ATTRIBUTE, mod) ; c o n t e x t . m o d i f y A t t r i b u t e s ( getGroupDN ( groupName ) , mods ) ; } catch ( A t t r i b u t e I n U s e E x c e p
t i o n e ) { // I g n o r e t h e a t t r i b u t e i f i t i s a l r e a d y a s s i g n e d } } . } // end c l a s s A revokePermission() metódus Egy jogelemet visszavesz a megadott csoportból, aminek működése olyan, mint a removeUser() volt. 121 Java programozói könyvtár 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Az Active Directory elérése . public c l a s s LDAPManager { . public void r e v o k e P e r m i s s i o n ( S t r i n g groupName , S t r i n g permissionName ) throws NamingException { try { M o d i f i c a t i o n I t e m [ ] mods = new M o d i f i c a t i o n I t e m [ 1 ] ; A t t r i b u t e mod = new B a s i c A t t r i b u t e ( " u n i q u e P e r m i s s i o n " , getPermissionDN ( permissionName ) ) ; mods [ 0 ] = new M o d i f i c a t i o n I t e m ( Di r C o n t e x t .REMOVE ATTRIBUTE, mod) ; c o n t e x t . m o d i f y A t t r i b u t e s ( getGroupDN ( groupName ) , mods ) ; } catch ( N o S u c h A t t r i b u t e E x
c e p t i o n e ) { // I g n o r e e r r o r s i f t h e a t t r i b u t e doesn ’ t e x i s t } } . } // end c l a s s A hasPermission() metódus A hasPermission() logikai metódus azt vizsgálja meg, hogy egy csoport rendelkezik-e egy joggal. Az implementáció logikája a userInGroup()-hoz hasonló. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 . public c l a s s LDAPManager { . public boolean h a s P e r m i s s i o n ( S t r i n g groupName , S t r i n g permissionName ) throws NamingException { // S e t up a t t r i b u t e s t o s e a r c h f o r S t r i n g [ ] s e a r c h A t t r i b u t e s = new S t r i n g [ 1 ] ; searchAttributes [ 0 ] = " uniquePermission " ; Attributes attributes = c o n t e x t . g e t A t t r i b u t e s ( getGroupDN ( groupName ) , searchAttributes ) ; i f ( a t t r i b u t e s != n u l l ) { A t t r i b u t e permAtts = a t t r i b u t e s . g e t ( " u n i q u e P e r m i s s i o n " ) ;
i f ( permAtts != n u l l ) { f o r ( NamingEnumeration v a l s = permAtts . g e t A l l ( ) ; v a l s . hasMoreElements ( ) ; ) { i f ( permissionName . e q u a l s I g n o r e C a s e ( getPermissionCN ( ( S t r i n g ) v a l s . nextElement ( ) ) ) ) { return true ; } } } } } return f a l s e ; . } // end c l a s s 122 Java programozói könyvtár Az Active Directory elérése A getPermissions() metódus A getPermissions() egy listát ad vissza mindazon jogelemekről, ami a megadott csoporthoz van éppen rendelve. A működés a getMembers()-hez hasonló 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 . public c l a s s LDAPManager { . public L i s t g e t P e r m i s s i o n s ( S t r i n g groupName ) throws NamingException { L i s t p e r m i s s i o n s = new L i n k e d L i s t ( ) ; // S e t up a t t r i b u t e s t o s e a r c h f o r S t r i n g [ ] s e a r c h A t t r i b u t e s = new S t r i n g [ 1 ] ; searchAttributes [ 0 ] = "
uniquePermission " ; Attributes attributes = c o n t e x t . g e t A t t r i b u t e s ( getGroupDN ( groupName ) , searchAttributes ) ; i f ( a t t r i b u t e s != n u l l ) { A t t r i b u t e permAtts = a t t r i b u t e s . g e t ( " u n i q u e P e r m i s s i o n " ) ; i f ( permAtts != n u l l ) { f o r ( NamingEnumeration v a l s = permAtts . g e t A l l ( ) ; v a l s . hasMoreElements ( ) ; p e r m i s s i o n s . add ( getPermissionCN ( ( S t r i n g ) v a l s . nextElement ( ) ) ) ) ; } } } return p e r m i s s i o n s ; . } // end c l a s s Egy valós példa Befejezésül egy éles rendszerből vett AD (LDAP) példával szeretnénk zárni ezt a fejezetet. A lent látható ModifyFields class egy POJO, ami egy indexet és egy stringet képes eltárolni. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package t e s t . ad ; public c l a s s M o d i f y F i e l d s { private i n t i n d e x ; private S t r i n g a d A t t r i b u t e ; public i n t
g e t I n d e x ( ) { return i n d e x ; } public void s e t I n d e x ( i n t i n d e x ) { this . index = index ; } public S t r i n g g e t A d A t t r i b u t e ( ) { return a d A t t r i b u t e ; } } public void s e t A d A t t r i b u t e ( S t r i n g a d A t t r i b u t e ) { this . adAttribute = adAttribute ; } 123 Java programozói könyvtár Az Active Directory elérése A fenti osztályt használja az LdapUtils class, aminek a feladata egy AD programozási felületet adni az alkalmazás felé. Az AD elérhető a szokásos LDAP porton keresztül (általában 389), de létezik egy Global Catalog szolgáltatás is, aminek segítségével egyszerre láthatjuk az összes Windows domain-t. Ez utóbbi alapértelmezett portja a 3268 A konstruktor elkészíti az LDAP (ctx ) és a Global Catalog (ctxGC ) kontextust is. A modifyUser() metódus feladata a megadott user DN specifikált attribútumainak módosítása. A 61-63 sorokban a Global Catalog-ban (GC) történik egy
keresés, ami csak person ÉS user lehet, valamint a megadott filter jellemző. A keresés eredménye a result lista, amin egy ciklussal (65-84 sorok) megyünk végig és feldolgozzuk. Ez a szükséges jellemzők módosítását jelenti, amely műveletet már a normál ctx LDAP kontextus segítségével végezzük, mert a GC nem alkalmas írásra. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package t e s t . ad ; import import import import import java . u t i l ArrayList ; java . u t i l Hashtable ; j a v a x . naming Context ; j a v a x . naming NamingEnumeration ; j a v a x . naming d i r e c t o r y ∗ ; import o r g . apache l o g 4 j Logger ; public c l a s s L d a p U t i l s { D i rC on te xt c t x = n u l l ; D i rC on te xt ctxGC = n u l l ; S e a r c h C o n t r o l s ctlsGC = n u l l ; Logger l o g ; public L d a p U t i l s ( ConnectType ldapConnect , Logger l o g g
e r ) throws E x c e p t i o n { log = logger ; l o g . i n f o ( "START␣ L d a p U t i l s ␣ c o n s t r u c t o r " ) ; Hashtable <S t r i n g , S t r i n g > env = new H a s h t ab l e <S t r i n g , S t r i n g >() ; env . put ( Context INITIAL CONTEXT FACTORY, ldapConnect . g e t I n i t i a l C o n t e x t F a c t o r y ( ) ) ; env . put ( Context PROVIDER URL, ldapConnect g e t P r o v i d e r U r l ( ) ) ; env . put ( Context SECURITY AUTHENTICATION, ldapConnect . g e t S e c u r i t y A u t h e n t i c a t i o n ( ) ) ; env . put ( Context SECURITY PRINCIPAL, ldapConnect g e t S e c u r i t y P r i n c i p a l ( ) ) ; env . put ( Context SECURITY CREDENTIALS, ldapConnect . g e t S e c u r i t y C r e d e n t i a l s ( ) ) ; c t x = new I n i t i a l D i r C o n t e x t ( env ) ; // f o r G l o b a l C a t a l o g Hashtable <S t r i n g , S t r i n g > envGC = new H a s h ta b l e <S t r i n g , S t r i n g >() ; envGC . put (
Context INITIAL CONTEXT FACTORY, ldapConnect . g e t I n i t i a l C o n t e x t F a c t o r y ( ) ) ; envGC . put ( Context PROVIDER URL, ldapConnect getGCProviderUrl ( ) ) ; envGC . put ( Context SECURITY AUTHENTICATION, ldapConnect . g e t S e c u r i t y A u t h e n t i c a t i o n ( ) ) ; envGC . put ( Context SECURITY PRINCIPAL, ldapConnect . g e t S e c u r i t y P r i n c i p a l ( ) ) ; envGC . put ( Context SECURITY CREDENTIALS, ldapConnect . g e t S e c u r i t y C r e d e n t i a l s ( ) ) ; ctxGC = new I n i t i a l D i r C o n t e x t ( envGC ) ; String [ ] attrIDs = { " distinguishedName " } ; ctlsGC = new S e a r c h C o n t r o l s ( ) ; ctlsGC . s e t R e t u r n i n g A t t r i b u t e s ( a t t r I D s ) ; 124 Java programozói könyvtár 51 52 53 54 55 56 57 58 59 60 61 } ctlsGC . s e t S e a r c h S c o p e ( S e a r c h C o n t r o l s SUBTREE SCOPE) ; l o g . i n f o ( "END␣ L d a p U t i l s ␣ c o n s t r u c t o r " ) ;
public void modifyUser ( ConnectType ldapConnect , S t r i n g [ ] row , i n t f i l t e r F i l e C o l u m n I n d e x , A r r a y L i s t <M o d i f y F i e l d s > m o d i f y F i e l d s ) throws E x c e p t i o n { l o g . i n f o ( "START␣ modifyUser " ) ; l o g . i n f o ( " Email : ␣ " + row [ f i l t e r F i l e C o l u m n I n d e x ] ) ; NamingEnumeration<S e a r c h R e s u l t > r e s u l t = ctxGC . s e a r c h ( " " , " (&( o b j e c t C a t e g o r y=å p e r s o n ) ( o b j e c t C l a s s=u s e r ) ( " + ldapConnect . g e t F i l t e r A t t r i b u t e ( ) + "=" + row [ f i l t e r F i l e C o l u m n I n d e x ] + " ) ) " , ctlsGC ) ; 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 while ( r e s u l t . hasMoreElements ( ) ) { SearchResult s e a r c h R e s u l t = r e s u l t . next ( ) ; S t r i n g d i s t i n g u i s h e d N a m e = s e a r c h R e s u l t . getNameInNamespace ( )
; l o g . i n f o ( " DistinguishedName : ␣" + distinguishedName ) ; i f ( d i s t i n g u i s h e d N a m e != n u l l ) { M o d i f i c a t i o n I t e m [ ] mods = new M o d i f i c a t i o n I t e m [ m o d i f y F i e l d s . size () ] ; f o r ( i n t i = 0 ; i < m o d i f y F i e l d s . s i z e ( ) ; i ++) { M o d i f y F i e l d s mf = m o d i f y F i e l d s . g e t ( i ) ; mods [ i ] = new M o d i f i c a t i o n I t e m ( D i r C o n t e x t .REPLACE ATTRIBUTE, new å BasicAttribute ( mf . g e t A d A t t r i b u t e ( ) , rowå [ mf . g e t I n d e x ( ) ] ) ) ; } 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 Az Active Directory elérése } else { } } c t x . m o d i f y A t t r i b u t e s ( d i s t i n g u i s h e d N a m e , mods ) ; l o g . i n f o ( " U n s u c c e s s f u l ␣ update ! ␣ There ␣ i s ␣ not ␣ d i s t i n g u i s h e d N a m e ! " ) å ; } } l o g . i n f o ( "END␣ modifyUser
" ) ; public void r e l e a s e A l l ( ) { try { i f ( c t x != n u l l ) { ctx . c l o s e () ; } i f ( c t x != n u l l ) { ctxGC . c l o s e ( ) ; } } catch ( E x c e p t i o n e ) { log . i n f o ( " Error ␣ r e l e a s i n g ␣ r e s o u r c e s ! " , e ) ; } } A Java és AD kapcsolatáról javasoljuk ezt a 2 webhelyet is áttanulmányozni: • JCIFS: http://jcifs.sambaorg/ • http://www.javaactivedirectorycom/ 125