Igaz, hogy már több, mint egy éve megjelent a 7-es Java, de még mindig nem késő megismerkedni az újdonságaival. Egész konkrétan az új íves és hajlított építőelemekre gondolok.
Az Effective Java című könyvben is szerepel, hogy nem ajánlott nullt visszaadni, inkább részesítsük előnyben az üres tömböket és az üres kollekciókat, ha tömb vagy kollekció a visszatérési érték típusa. Lentebb arra látható egy példa, hogy JAXB használatakor hogyan kell annotálni az osztályunkat, hogy az unmarshall után a JAXB ne hagyjon maga után nullokat a kollekciók értékeként.
import java.util.LinkedList; import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) public class Data { @XmlElementWrapper(name = "list") @XmlElement(name = "list-element") private final List<String> list = new LinkedList<String>(); public Data() { } public List<String> getList() { return new LinkedList<String>(list); } public void setList(final List<String> list) { if (list == null) { throw new IllegalArgumentException("list cannot be null"); } this.list.clear(); this.list.addAll(list); } }
Érdemes megjegyezni, hogy ha a getter metódusra kerülne a @XmlElement annotáció, akkor a metódus által visszaadott listába kerülnének a JAXB által felvett elemek, emiatt a getterben nem lehetne másolatot visszaadni a belső listáról.
A lenti EasyMock (3.0) tesztben átadunk az executornak egy Runnable példányt. Ez egy mock objektum, és talán azt várnánk, hogy a verify() majd jelzi, hogy az executor (egy másik szálban) engedély nélkül meghívta a mock objektum run() metódusát, azaz hibával elszáll a teszt.
import static org.junit.Assert.assertTrue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.easymock.EasyMock; import org.junit.Test; public class EasyMockTest { @Test public void testEasyMock() throws Exception { final Runnable runnable = EasyMock.createMock(Runnable.class); EasyMock.makeThreadSafe(runnable, true); // default true EasyMock.replay(runnable); runInNewThread(runnable); EasyMock.verify(runnable); // should fail } private void runInNewThread(final Runnable runnable) throws InterruptedException { final ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(runnable); executor.shutdown(); final boolean terminated = executor.awaitTermination(2, TimeUnit.SECONDS); assertTrue("terminated", terminated); } }
Nem ez történik, a teszt hiba nélkül lefut, a verify() nem jelez hibát. A konzolon viszont megjelenik a hibaüzenet:
Exception in thread "pool-1-thread-1" java.lang.AssertionError: Unexpected method call run(): at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:45) at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:73) at $Proxy4.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619)
JMock-kal (2.5.1) ugyanez a helyzet:
import static org.junit.Assert.assertTrue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.jmock.Mockery; import org.jmock.integration.junit4.JMock; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(JMock.class) public class JmockTest { private Mockery mockery; @Before public void setUp() { mockery = new Mockery(); } @Test // should fail public void testJmock() throws Exception { final Runnable runnable = mockery.mock(Runnable.class); runInNewThread(runnable); } ... }
A teszt sikeresen lefut, a hibaüzenet itt is csak a hibakonzolon jelenik meg.
Mockitót (1.8.5) használva jobbak az eredmények:
import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.junit.Test; public class MockitoTest { @Test public void testMociko() throws Exception { final Runnable runnable = mock(Runnable.class); runInNewThread(runnable); try { verify(runnable, never()).run(); // failed fail("verify"); } catch (final AssertionError expected) { } } ... }
A verify() hívás hibát dob, ahogy vártuk. A konzolon pedig nem jelenik meg semmi, mert mock() metódus úgynevezett nice mock objektumokat készít, amelyek csak rögzítik a meghívott metódusaikat és sosem dobnak hibát.
Az EasyMock-hoz egy nem túl szép workaround saját ThreadFactory használata a kivételt elkapó UncaughtExceptionHandler-rel, majd a teszt végén az UncaughtExceptionHandler ellenőrzése.
import static org.junit.Assert.assertTrue; import java.lang.Thread.UncaughtExceptionHandler; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.easymock.EasyMock; import org.junit.Test; public class EasyMockExceptionHandlerTest { @Test public void testEasyMockWithUncaughtExceptionHandler() throws Exception { final Runnable runnable = EasyMock.createMock(Runnable.class); EasyMock.makeThreadSafe(runnable, true); // default true EasyMock.replay(runnable); final AtomicBoolean threadError = new AtomicBoolean(false); final ExecutorService executor = createExecutor(threadError); executor.execute(runnable); executor.shutdown(); final boolean terminated = executor.awaitTermination(2, TimeUnit.SECONDS); assertTrue("terminated", terminated); EasyMock.verify(runnable); assertTrue("thread error", threadError.get()); } private ExecutorService createExecutor(final AtomicBoolean threadError) { final ErrorFlagUncaughtExceptionHandler uncaughtExceptionHandler = new ErrorFlagUncaughtExceptionHandler(threadError); final ThreadFactory threadFactory = new ThreadFactory() { final ThreadFactory defaultThreadFactory = Executors .defaultThreadFactory(); public Thread newThread(final Runnable runnable) { final Thread thread = defaultThreadFactory.newThread(runnable); Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler); return thread; } }; final ExecutorService executor = Executors .newSingleThreadExecutor(threadFactory); return executor; } private class ErrorFlagUncaughtExceptionHandler implements UncaughtExceptionHandler { private final AtomicBoolean threadError; public ErrorFlagUncaughtExceptionHandler(final AtomicBoolean threadError) { this.threadError = threadError; } public void uncaughtException(final Thread thread, final Throwable throwable) { threadError.set(true); } } }
JMock-hoz talán a 2.6-os vagy 2.7-es verzióban lesz erre megoldás, a jelenlegi dokumentáció alapján nem igazán támogatott a JMock mock objektumainak többszálú használata. Lásd még: JMOCK-213,
JMOCK-183.
EasyMock: During the replay phase, mocks are by default thread-safe.
Ugyanez a helyzet a Mockitónál is: You can let multiple threads call methods on a shared mock to test in concurrent conditions.
Az igazi megoldást azonban nem mock-olásra használt osztálykönyvtárban kellene implementálni, hanem a tesztelt kódban, hogy a más szálakon keletkező hibákról a hívó értesüljön, valamint a tesztünk is ezen az úton tudja meg, hogy hiba történt. Az EasyMock levelezőlistáján olvasható erről egy levél:
„Let's say the exception would be thrown by a real implementation, and *not* by EasyMock. How would your application know that the calculation went wrong?”
Erre használható például az ExecutorService:
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.easymock.EasyMock; import org.junit.Test; public class ExecutorServiceTest { @Test(expected = ExecutionException.class) public void testEasyMockWithExecutorService() throws Exception { final Runnable runnable = EasyMock.createMock(Runnable.class); EasyMock.makeThreadSafe(runnable, true); // default true EasyMock.replay(runnable); final ExecutorService executorService = Executors .newSingleThreadExecutor(); final Future> future = executorService.submit(runnable); future.get(); } }
Egy augusztusi blogbejegyzés szerint a Sonatype Maven by Example című könyve letölthető epub formátumban is, azonban a bejegyzésben lévő link már él. Ettől függetlenül a könyv github-os repositoryjából letölthető a forrás, amiből Maven segítségével könnyedén elkészíthető az epub fájl.
Sajnos az így előállított epub nem tökéletes, az epubcheck talál benne néhány hibát. Többségüket a Sigil képes automatikusan javítani (hiányzó, egyébként nem használt, többnyire pdf fájlokra mutató hivatkozások törlése a content.opf-ből), valamint a pár hiányzó képet kézzel is berakhatjuk az epub fájlba (még Sigillel történő megnyitás előtt!). A PRS-650-esen a borítóval voltak még bajaim. A képet kisebbre méretezve ez is megoldódott. Valamint még néhány, többnyire üres xml:lang attribútumot kellett törölnöm, hogy érvényes legyen az epub fájl.
Egyébként nem biztos, hogy Sigillel érdemes ezeket a hibákat javítani. Nálam a 0.3.4-es verzió elrontotta a tartalomjegyzéket. A javítás nem volt bonyolult, a projekt target könyvtárából kikerestem a toc.ncx-et, kézzel visszamásoltam, majd javítottam (mindet cserél funkció) a Sigil által elmozgatott fájlokra mutató hivatkozásokat.
Jó hír, hogy a Maven By Example könyvön kívül másik három Sonatype könyv is elérhető epub formátumban. Repositoryk sorban:
Letöltés és mvn install után az epubos alprojekt target mappájának mélyén megtalálható valahol az elkészített epub fájl. A letöltéshez és a fordításhoz megfelelő mennyiségű szabad hely nem árt. Némelyik projekt fordítás utáni mérete alulról súrolja a két gigabájtot.
A Sonatype-os repositoryk között egyéb olvasnivalókat is találhatunk. Ott van a Maven könyv régebbi változata, egy Maven szakácskönyv, valamint egy Hudson könyv első fejezete, de a fizetős Sonatype programokhoz tartozó dokumentáció is akad.
Ha Windows alatt megpróbálunk törölni egy olyan fájlt amit valamelyik másik program még használ, akkor hibaüzenetet kapunk, a fájl pedig marad a helyén. Linux/Unix alatt a fájl eltűnik a fájllistából, de ténylegesen csak akkor törlődik amikor az utolsó azt használó program is bezárta a fájlt.
NFS-en ez másképp van: a fájl rögtön törlődik. Ha valaki épp használja akkor a következő olvasásnál IOException-t fog kapni a fenti, Stale NFS file handle hibaüzenettel.
Forrás: Lucene in Action, 2nd edition
A JackRabbit-es Node, Item és Property osztályok nem implementálják az equals() és hashCode() metódusokat, azokat az Object-től öröklik. Így nem érdemes Set-be rakni ezen objektumok példányait, hiszen többször is szerepelhet ugyanarra az elemre mutató objektum a halmazban.
Emiatt a kódban minden Set
@Test public void testSimpleRootNodeEquals() throws RepositoryException { final Node rootNode1 = session.getRootNode(); final Node rootNode2 = session.getRootNode(); assertTrue(rootNode1.isSame(rootNode2)); // sajnos ez false, mert a Node nem implementálta az equals/hashCode // metódusokat assertFalse(rootNode1.equals(rootNode2)); }
Kollégák kiskapus fordításokkal kapcsolatos kritikus véleménye ellenére magyarul olvastam a fenti könyvet. Az utóbbi időben szinte csak angol szakirodalommal találkoztam, a magyar kiadásokat csak könyvtárban futottam át. A Tiszta kód esetében reménykedtem, hogy a sok csúszásnak jó minőségű fordítás lesz a vége. (Ha jól emlékszem, először decemberre ígérték, amiből április vége lett.) Nem jött be, inkább eredetiben kellett volna elolvasni. Ehelyett lett három bejegyzés a blogon. (Tiszta kód angol-magyar szótár, Tiszta kód hibajegyzék)
A könyv címe Tiszta kód. A magyar változatban ne számítsunk arra, hogy a példakódok tényleg tiszták, mert újra lettek tördelve. A tiszta kódok rondák, a rondának készült kódok meg még rondábbak. (Kettőt kiemelnék: 5.1-es, 5.5-ös példa. Bár az összes többi is hasonlóan rossz.)
Eltűntek az üres sorok, egyes sorok véletlenszerűen egy karakterrel bentebb kezdődnek. Ilyen például a 101. oldal, ahol a double kezdetű sorok előtt felesleges a szóköz. A hosszú sorok pedig nem fértek ki egy sorba. Az eredeti műben ezzel nem volt probléma.
Sajnos a legjobb szándékkal sem lehet azt mondani, hogy szerkesztési, illetve helyspórolási okokból volt szükség ezekre a módosításokra, mert olyan helyeken is áttördelték a kódot, ahol semmi szükség nem lett volna rá. (Ilyenek a 98. oldal metódusai, ahol kimaradtak az üres sorok, pedig még lett volna hely azoknak is.)
Ezen kívül az erőltetett magyarítások is elég fárasztóak, megnehezítik a megértést. (Lásd a másik két bejegyzést a szótárral és a hibákkal. Különösen azokat, amire a Google is minimális számú vagy éppen nulla találatot adott.) Túlságosan frusztráló azon agyalni, hogy vajon mit is akarhatott a szerző, amiből egy egyáltalán nem magától értetődő magyar kifejezés született.
Azt hiszem ez volt az utolsó kiskapus fordítás, amit olvastam. Mondhatni betette a kaput... Még akkor is, ha az angol nyelvű könyvek valamivel nehezebben elérhetőek a magyar piacon.
A könyv egyébként nagyon jó, érdemes elolvasni. A másik két bejegyzés remélhetőleg segít a döntésben, hogy az angolt vagy a magyart válaszd. Én az angolra szavazok.
Kigyűjtött magyarított kifejezések és angol megfelelőik a Clean code c. könyv magyar kiadásából. Avagy: Kiskapu-angol szótár. Azért, hogy legalább egy találatot dobjanak mindegyikre a keresők. Persze vannak benne teljesen elfogadott fordítások, de olyanok is, amitől falnak tudnék menni. Az oldalszámok a címben is említett Tiszta kód című könyv magyar fordítására vonatkoznak.
1. fejezet, Tiszta kód
2. fejezet, Beszédes nevek
4. fejezet, Megjegyzések
5. fejezet, Formázás
6. fejezet, Objektumok és adatszerkezetek
7. fejezet, Hibakezelés
8. fejezet, Határok
9. fejezet, Egységtesztek
10. fejezet, Osztályok
11. fejezet, Rendszerek
13. fejezet, Párhuzamosság
14. fejezet, Fokozatos finomítás
15. fejezet, A JUnit belső részletei
16. fejezet, A SerialDate újratervezése
17. fejezet, Szagok és szabályok
A függelék: Párhuzamosság II
A Tiszta kód című könyvben (Robert C. Martin, 2010, ISBN: 9789639637696) általam felfedezett hibák listája alább.
1. fejezet, Tiszta kód
2. fejezet, Beszédes nevek
5. fejezet, Formázás
6. fejezet, Objektumok és adatszerkezetek
7. fejezet, Hibakezelés
8. fejezet, Határok
11. fejezet, Rendszerek
13. fejezet, Párhuzamosság
14. fejezet, Fokozatos finomítás
17. fejezet, Szagok és szabályok
Továbbá fejezettől függetlenül (talán) az összes forráskód át lett tördelve. A szép kódok csúnyák lettek, a csúnyának szántak pedig még rondábbak. Pedig ebben a könyvben pont a szép kódokon van (illetve lenne) a hangsúly.
Néhány lejárófélben lévő voucher miatt beneveztem egy Sun Certified Business Component Developer (SCBCD) for the Java Platform, Enterprise Edition 5 (CX-310-091) vizsgára is nemrég. A Java EE 6 vizsgákat tavaszra ígérték a megvásárolt Sun weblapján a tranzakció előtt, azóta viszont nem sok újdonságot hallani a témáról, úgyhogy kénytelen voltam az 5-össel beérni.
Szokásos emlékeztetők lentebb. Tesztelésre, példakódok írására egy régebbi, egész pontosan 6.7.1-es Netbeanst és a hozzá csomagolt GlassFish 2.1.1-et használtam.
Tanulni leginkább a specifikációból érdemes, a vizsgán is azt kérdezik. A GlassFish alatt kipróbáltakra sem érdemes túlságosan hagyatkozni, mert néhol megengedőbb, mint a specifikáció. Nem mindig veszi szigorúan a leírtakat az implementáció.
A felkészüléshez legnagyobb segítséget az Enthuware-es tesztprogram jelentette. Korrekt darab, jó kérdésekkel, megéri az árát, sok időt lehet vele spórolni. A SkillGuru-n lévő kérdéssorral viszont csak óvatosan, szerintem elég sok hiba van benne.
Könyvek közül az EJB 3 in Action-re esett a választásom. Leginkább ezt olvastam végig, de volt nálam egy Enterprise JavaBeans 3 is. Utóbbi inkább referenciaként van felépítve. XML-es részekben az O'Reilly-féle a nyerő, bár szerencsére vizsgán nem túl sok XML-t kérdeztek, nem igazán volt szükség a DTD-k bemagolására. Ami kellett, az kb. a manningos könyvben is benne volt.
Leírom ide is: A lista átnézése a könyvekben és a JavaRanch-en linkelt vizsgasorok, feladatok megoldása előtt túlzott önbizalomhoz vezethet, aminek sikertelen vizsga lehet a vége.
Hivatkozások:
Legutóbbi hozzászólások
8 év 12 hét
9 év 1 hét
9 év 5 hét
9 év 23 hét
10 év 25 hét
10 év 31 hét
10 év 31 hét
10 év 32 hét
10 év 42 hét
11 év 12 hét