<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7271455030734972271</id><updated>2012-02-14T17:06:47.289+01:00</updated><category term='CodeModel'/><category term='OSGi'/><category term='MySQL'/><category term='SQL'/><category term='Trigger'/><category term='AJAX'/><category term='Security'/><category term='Java'/><category term='Log4j'/><category term='Trace'/><category term='Collection'/><category term='JDBC'/><category term='EJB'/><category term='Portlet'/><category term='Bytecode'/><category term='Database'/><category term='Linux'/><category term='Btrace'/><category term='Debug'/><category term='SSL'/><category term='Apache'/><category term='Glassfish'/><category term='JavaScript'/><category term='Liferay'/><category term='JSON'/><category term='J2EE'/><category term='CoolRMI'/><title type='text'>java'nother blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>28</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-6823046059502287006</id><published>2012-01-21T17:29:00.001+01:00</published><updated>2012-01-23T13:12:37.560+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Weak és Soft referenciák a Javaban</title><content type='html'>&lt;p&gt;Több éves Java tapasztalattal a hátam mögött hallottam először a gyenge (weak) referenciák létezéséről a nyelvben, és úgy gondoltam érdemel pár szót a téma kör. Javaslom, akinek nem világos a JVM szemétgyűjtőjének (továbbiakban GC) működése, ezen a ponton olvassa el Viczián István &lt;a href="http://jtechlog.blogspot.com/2011/12/java-memoriakezeles-szemetgyujto.html"&gt;idevágó&lt;/a&gt; bejegyzését. Tehát mint tudjuk a GC kidob a memóriából minden olyan objektumot, melyre nem mutat már egyetlen referencia sem. Amikor létrehozunk egy objektumot, Integer foo = new Integer(0), akkor a foo egy strong reference lesz rá, és amíg az objektum "strongly reachable", addig a GC-nek tabu. Mivel ezeket az objektumokat nem tudja a GC felszabadítani, a memória beteltével jön a fránya OutOfMemotyError, és az alkalmazás kilép. Egy darabig persze lehet növelni a memóriát, majd a fizikai határok elérésével lehet elosztani az alkalmazást, de előbb álljunk meg egy szóra! Vajon minden objektumra szükségünk van a memóriában? Kézenfekvő megoldás, hogy bizonyos objektumokat azonnal megszüntessünk, amint nincs rájuk szükség, viszont az objektumok újbóli létrehozása is nagy költség, ebben az esetben pedig értékes processzoridőt fecsérelünk az állandó memóriaallokációra, példányosításra, stb. Arany középútként az 1.2-es, igen az 1.2-es Java verzióban bevezették a weak referenciákat, melynek implementációja a &lt;a href="http://docs.oracle.com/javase/6/docs/api/java/lang/ref/WeakReference.html"&gt;WeakReference&lt;/a&gt; osztály. A WeakReference referenciát tárol az adott objektumra, annyi különbséggel, hogy ezt az objektumot a GC első futáskor, szó nélkül eltávolítja a memóriából, felszabadítva ezzel a helyet mások számára.&lt;/p&gt;
&lt;pre class="brush: java;"&gt;
WeakReference&amp;lt;StringBuilder&amp;gt; weakString = new WeakReference&amp;lt;StringBuilder&amp;gt;(new StringBuilder());

int i = 0;
while (weakString != null &amp;&amp; weakString.get() != null) {
 weakString.get().append(i++);
 System.out.println(i);
}

System.out.println("Finish");
&lt;/pre&gt;

&lt;p&gt;Kezdetként futási paraméterként állítsuk be a JVM-nek a maximális memóriát mondjuk 2 Mb-ra (-Xmx2m), majd az értékkel játszva láthatjuk, hogy mennyit változik programunk kimenete. Ez a technika kiválóan alkalmas memória gyorsítótárak készítésére, ám amennyiben kulcs-érték-pár alapú gyorsítótárat készítünk a new WeakReference(myMapInstance) helyett használjuk, az erre a célra készített &lt;a href="http://docs.oracle.com/javase/6/docs/api/java/util/WeakHashMap.html"&gt;WeakHashMap&lt;/a&gt; implementációt. A WeakHashMap a kulcsokat őrzi weak referenciával, és a MapEntry automatikusan eltávolításra kerül, amikor a kulcs már nincs rendszeres használat alatt.&lt;/p&gt;
&lt;p&gt;A WeakReference mellett létezik a &lt;a href="http://docs.oracle.com/javase/6/docs/api/java/lang/ref/SoftReference.html"&gt;SoftReference&lt;/a&gt; osztály is, amely azt garantálja, hogy az OutOfMemoryError előtt felszámolásra kerülnek az objektumok, helyet biztosítva az újonan létrejövőknek.
&lt;pre class="brush: java;"&gt;
SoftReference softObj = new SoftReference(new Serializable() {
 @Override public void finalize() throws Throwable {
  System.out.print("Finalize runned");

  super.finalize();
 } 
});

StringBuilder sb = new StringBuilder("foobar");
while (true) {
  sb.append(sb.toString());
}
&lt;/pre&gt;
A kimenetben láthatjuk, hogy miközben az alkalmazás akkut memóriahiányban elhalálozott, még utolsó leheletével felszámolta osztályunkat.
&lt;pre&gt;
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
&lt;b&gt;Finalize runned&lt;/b&gt; at java.util.Arrays.copyOf(Arrays.java:2882)
 at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100)
 at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:390)
 at java.lang.StringBuilder.append(StringBuilder.java:119)
 at com.blogspot.jpattern.Main.run(Main.java:36)
 at com.blogspot.jpattern.Main.main(Main.java:22)
Java Result: 1
&lt;/pre&gt;
Természetesen ennél complexebb program esetében az alkalmazás tovább tud dolgozni a felszabadult memóriával.&lt;b&gt;A Java specifikáció szerint semmi garancia nincs a finalize() metódus futására!&lt;/b&gt;
&lt;/p&gt;
&lt;p&gt;Evezzünk egy kicsit sötétebb vizekre. Ha jól megnézzük a &lt;a href="http://docs.oracle.com/javase/6/docs/api/java/lang/ref/Reference.html"&gt;Reference&lt;/a&gt; API dokumentációját, láthatjuk, hogy van még egy ismert implementáció, a &lt;a href="http://docs.oracle.com/javase/6/docs/api/java/lang/ref/PhantomReference.html"&gt;PhantomReference&lt;/a&gt;. Az első különbség társaihoz képest, hogy a bele helyezett objektumra soha nem tudunk referenciát kérni, ugyanis konstans null értékkel válaszol a get() hívására. Másik nagy eltérés lényege tömören, hogy az ilyen objektumokat a GC csak azelőtt rendezi sorba, mielőtt a fizikai memóriából kitakarítaná. Mivel úgy tudom &lt;a href="http://en.wikipedia.org/wiki/Darth_Vader"&gt;Darth Vader&lt;/a&gt; nagyurat is ennek használata állította át az erő sötét oldalára, én magam nem merészkedtem ennél tovább (esetleg írhatnátok valami konkrét használati esetet).&lt;/p&gt;
&lt;p&gt;Előfordulhat, hogy az alkalmazásunkban szükséges tudni, hogy mely objektumokat dobta már ki a GC, és melyeket nem. Ilyen esetben egy &lt;a href="http://docs.oracle.com/javase/6/docs/api/java/lang/ref/ReferenceQueue.html"&gt;ReferenceQueue&lt;/a&gt; osztályt kell példányosítanunk, és a queue példányt átadni a WeakReference vagy SoftReference konstruktorának. A bejegyzésben említett referenciákkal óvatosan bánjunk, és csak alapos tervezés és tesztelést követően alkalmazzuk éles bevetésben őket.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-6823046059502287006?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/6823046059502287006/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2012/01/weak-es-soft-referenciak-javaban.html#comment-form' title='2 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/6823046059502287006'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/6823046059502287006'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2012/01/weak-es-soft-referenciak-javaban.html' title='Weak és Soft referenciák a Javaban'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-8117162702394282163</id><published>2011-12-12T11:31:00.001+01:00</published><updated>2011-12-28T15:53:26.706+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='J2EE'/><category scheme='http://www.blogger.com/atom/ns#' term='Portlet'/><category scheme='http://www.blogger.com/atom/ns#' term='Liferay'/><title type='text'>Liferay Service Builder, a sötét oldal</title><content type='html'>Korábban már volt szó a &lt;a href="http://jpattern.blogspot.com/2010/11/liferay-servide-builder.html"&gt;Liferay Service Builder&lt;/a&gt;éről, akkor inkább a tudásáról esett szó, most pedig az árny oldalát szeretném taglalni. A Service Builder feladata, hogy egy egységes perzisztens réteget biztosítson anélkül, hogy a különböző WAR-ok eltérő kontextusaival, vagy az eltérő adatbázisok problémájával kellene bajlódnunk. A kezdetben kézenfekvő megoldások mára sajnos nem nyújtják azt a kényelmet, amit egy &lt;a href="http://en.wikipedia.org/wiki/Java_Persistence_API"&gt;JPA&lt;/a&gt;/&lt;a href="http://en.wikipedia.org/wiki/Java_Transaction_API"&gt;JTA&lt;/a&gt;-hoz szokott fejlesztő elvár egy efféle megoldástól. Lássuk mik azok a pontok, amiket én személy szerint fájlalok:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Az entitásokat leíró XML-ben csak primitív, String, vagy Date típust jelölhetünk meg az adatok tárolására, viszont osztály szinten a primitív adattagok inicializálódnak, tehát nem tudunk olyan mezőt felvenni, amelynek az értéke üres, vagyis NULL. Képzeljük el a User entitásunkat, egy opcionális életkor mezővel. Mivel az életkor alapértelmezetten 0 értéket vesz fel, nem tudjuk megkülönböztetni a csecsemő felhasználóinkat azoktól, akik nem töltötték ki az életkorukat. A példa légből kapott, de jól szemlélteti a primitív típusok hiányosságát. A Liferay készített egy workaroundot a probléma orvoslására, és használhatunk wrapper osztályokat is a primitívek helyett, viszont ebben az esetben szembesülnünk kell két dologgal, mégpedig, hogy a &lt;a href="http://en.wikipedia.org/wiki/Document_Type_Definition"&gt;DTD&lt;/a&gt; miatt kedvenc IDE-nk figyelmeztetni fog, hogy nem megfelelő adatot írtunk be, másrészt hivatalosan a Liferay nem támogatja ezt a megoldást.&lt;/li&gt;
&lt;li&gt;Rengetegszer futottam bele, hogy a &amp;nbsp;String mezők hossza alapértelmezetten 75 karakter széles. Ezen a ponton lehet vitatkozni, hogy valahol meg kellett húzni a határt az adatbázis méretének és a programozói munkának az optimalizációja közben, de véleményem szerint a 75 karakter a legtöbb esetben nem elég. Lehetett volna egy kicsit a felesleges adatbázis helyfoglalás felé billenteni a mérleget. A programozók feledékenyek, a tesztelők meg hanyagok, és ebből majdnem egyenesen következik, hogy 100 String mezőből legalább 1-nél csak túl későn derül ki, hogy nem elegendő a 75 karakter. Természetesen van megoldás, mégpedig az src/META-INF könyvtárban lévő &amp;nbsp;portlet-model-hints.xml fájlban kell a mezőkre "tippeket" adni a Liferaynek. Persze itt nem csak a mező szélességére adhatunk hasznos tanácsokat, egyéb dolgokat is beállíthatunk, ezeket most nem részletezném.&lt;pre class="brush: xml;"&gt;&lt;field name="value" type="String"&gt;&lt;hint-collection name="TEXTAREA"&gt;&lt;/hint-collection&gt;&lt;/field&gt;&lt;/pre&gt;
A megoldás árny oldala, hogy EE verzió esetén megcsinálja az adatbázis módosítását a rendszer, azonban az általam próbált CE verziók nem módosították, az adatbázist, és a módosítást végrehajtó SQL-t is magamnak kellett megírnom. Ezen a ponton bukott meg az adatbázis-függetlenség. Nem ennyire elkeserítő a helyzet, mert írhatunk olyan általános SQL-t, amit a Liferay is használ a Service Builder tábláinak legenerálásához, és létezik egy osztály a Liferayben, ami az általános SQL-ből adatbázisnak megfelelő SQL-t generál, amit egy startup hookba ágyazva futtathatunk, de azt hiszem ez túl nagy ár a függetlenségért.
&lt;/li&gt;
&lt;li&gt;A tranzakciók kezelése sem túl kifinomult szerintem a Liferayben. A Liferay filozófiája, hogy a tranzakciók kezelését teljesen a &lt;a href="http://en.wikipedia.org/wiki/Spring_AOP#Aspect-oriented_programming_framework"&gt;Spring&amp;nbsp;AOP&lt;/a&gt;-re&amp;nbsp;bízza, amely a Liferay szívében van konfigurálva. Az alapértelmezett működés, hogy az&amp;nbsp;add*, check*, clear*,&amp;nbsp;delete*, set*, és update* karakter-lánccal kezdődő metódusok egy tranzakciót indítanak. Ettől eltérően csak annyit tehetünk, hogy a service.xml-ben az entitásnál a tx-required mezővel megadhatjuk, hogy ezen felül milyen metódusok indítsanak tranzakciót. Nekem nagyon kényelmes és kézenfekvő, hogy szabadon válogathatok a&amp;nbsp;&lt;a href="http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Transaction3.html"&gt;6 féle tranzakció&lt;/a&gt;-kezelés közül, és bármikor eldönthetem, hogy becsatlakozok-e a meglévő tranzakcióba, hogy indítok-e újat, stb. Megint csak egy légből kapott példa, banki tranzakciót indítok a rendszerben, és a folyamat közben szeretném loggolni, hogy pontosan mi történt a tranzakcióval, de sajnos a log szerver timeoutol/betelik a lemez/stb. Ebben az esetben a loggolást végző kódrészlet hibája miatt gördül vissza az egész tranzakció, holott csak egy harmadlagos funkció nem teljesült. Létezik a Liferayben EXT plugin, amivel felül lehet írni a Spring konfigot, de a 7-es verziótól ezek egyáltalán nem lesznek támogatottak, így én sem javaslom senkinek, hogy ezt az utat válassza.&lt;/li&gt;
&lt;li&gt;A service réteg deleteAll metódusai a perzisztes rétegen keresztül egyesével törlik az entitásokat, ami egy 10M+ rekordszámú táblánál órákba is telhet. Ezt különösen azért nem értem, mert a Liferay nem kezel entitás-kapcsolatokat, tehát feleslegesnek érzem az adatbázisból egyesével kikérni a rekordokat, és törlést végrehajtani rajtuk. Szerencsére erre is van egy kiskapu, közvetlenül el tudunk kérni egy JDBC kapcsolatot, ahol szabadon garázdálkodhatunk:
&lt;pre class="brush: java;"&gt;DataAccess.getConnection.createStatement().execute("delete from MY_Entity");&lt;/pre&gt;
Természetesen a kapcsolat élet ciklusáról magunknak kell gondoskodnunk.&lt;/li&gt;
&lt;li&gt;Mint fentebb is írtam a Liferay nem igazán kezeli az entitás relációkat. Meg lehet ugyan adni kapcsolatokat, de a OneToOne kapcsolaton kívül, amit kezel a rendszer, minden kapcsolat OneToMany esetén &amp;nbsp;csak egy long típusú mező, ManyToOne esetén pedig egy üres Collection lesz. a OTO kapcsolatot az service.xml-ben a reference tag segítségével adhatjuk meg. Ennek célja egyébként a minél szélesebb adatbázis paletta támogatása, de szerény véleményem szerint ez akkor plusz terhet ró a fejlesztőkre, és annyi hiba lehetőséget, és adatbázis-szemetet eredményez, ami nem biztos, hogy megéri ezt az árat. További hátránya a szemléletnek, hogy így az entitások közötti kapcsolatot View oldalon kell lekezelni, onnan kell az adatbázishoz fordulni minden kapcsolat esetén. A Liferay egyébként MVC patternt követi, de vallja azt a nézetet, hogy lekérdezés esetén lehet közvetlenül a Model-hez fordulni, egyszerűsítve a logikát, így viszont egy standard Liferay portlet plugin jsp-je tele van scriptletekkel, olvashatatlanná téve a kódot. Ízlések és pofonok, én személy szerint szeretem, ha jól el vannak szeparálva a dolgok egymástól, a jsp szerkesztésekor az IDE tud hasznos tanácsokat adni, és nincs tele a Problems fül felesleges figyelmeztetésekkel, ha a Model önállóan végzi a dolgát, és nem a View fejlesztőnek kell gondolkodnia a különböző service hívásokról.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
Röviden ennyi jutott most eszembe a Liferay Service Builderének árny oldalairól, remélem ezzel nem vettem el senki kedvét a használattól, nem ez volt a célom. Véleményem szerint ha viszonylag nem túl nagy, hordozható, és/vagy SOAP-os megoldásra van szükség, jó választás lehet, de mielőtt nagy projekteket építenénk erre a komponensre mindenféleképpen végezzünk kutatásokat, kísérletezzünk, hogy kielégíti-e üzleti igényeinket maximálisan.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-8117162702394282163?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/8117162702394282163/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2011/12/liferay-service-builder-sotet-oldal.html#comment-form' title='4 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/8117162702394282163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/8117162702394282163'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2011/12/liferay-service-builder-sotet-oldal.html' title='Liferay Service Builder, a sötét oldal'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-1796117221397261845</id><published>2011-10-30T12:23:00.000+01:00</published><updated>2011-10-30T12:26:01.363+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Security'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Liferay'/><title type='text'>OWASP AntiSamy Javaban</title><content type='html'>&lt;p&gt;A webes biztonság kérdése egyidős magával az internettel, hiszen a hálózaton elérhető adatokat felhasználók milliói tekintik meg napról-napra. Mivel a weboldalak végeredményben a kliensek gépein futnak, hamar beláthatjuk, hogy a biztonsági kérdések nem elhanyagolhatóak, és mind a klienseknek, mind a weboldalak üzemeltetőinek fontos, hogy a kiszolgált tartalom hiteles és biztonságos legyen. Tipikus támadási forma az un. &lt;a href="http://en.wikipedia.org/wiki/Cross-site_scripting"&gt;XSS&lt;/a&gt;, amikor pl. egy az oldalba ágyazott külső Javascript próbál szenzitív információkhoz hozzájutni a böngésző valamely biztonsági hibáját kihasználva. Az ilyen és ehhez hasonló problémák megoldására jött létre 2001-ben &lt;a href="https://www.owasp.org/index.php/Category:OWASP_AntiSamy_Project"&gt;OWASP Antisamy Project&lt;/a&gt;, melynek célja nyílt forrású alternatívát, és szabványt kínálni az alkalmazások megvédésére.&lt;/p&gt;&lt;p&gt;Mint minden 3rd party fejlesztés, ez is úgy kezdődik, hogy &lt;a href="http://code.google.com/p/owaspantisamy/downloads/list"&gt;letöltjük&lt;/a&gt; a programkönyvtár lehetőleg legfrissebb verzióját. AntiSamy jelenleg az 1.4.4-es verziónál tart. A dokumentációval ellentétbe a csomag függőségeit nem lehet letölteni, rákeresve a antisamy-required-libs.zip kulcsszavakra találtam egy &lt;a href="http://code.google.com/p/owaspantisamy/downloads/detail?name=antisamy-required-libs-1.2.zip&amp;amp;can=1&amp;amp;q="&gt;oldalt&lt;/a&gt;. A készítők szerint deprecated ami a zip-be van, de a próba erejéig megteszi, éles használatra össze kell vadászni a függőségeket. A szoftver konfigurációja eléggé bonyolult, hosszas dokumentáció olvasással és tanulással egész biztos össze lehet dobni egy normális konfig XML-t, de szerencsére több nagyobb felhasználó is rendelkezésre bocsátotta saját összeállítását, így nyugodtan mazsolázgathatunk az eBay, MySpace, vagy akár Slashdot beállításaiból.&lt;/p&gt;&lt;p&gt;Az általam készített tesztprogram az alábbi (belecsempészve kis Java 7-et):&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: java;"&gt;import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import org.owasp.validator.html.*;


public class AntiSamyTest {

    public static void main(String[] args) {
        try {
            URL dirtyHtmlUrl = AntiSamyTest.class.getResource("dirtyHtml.html");
            Path dirtyHtmlPath = FileSystems.getDefault().getPath(dirtyHtmlUrl.getFile());
            List&amp;lt;String&amp;gt; lines = Files.readAllLines(dirtyHtmlPath, Charset.defaultCharset());
   
            URL configXmlUrl = AntiSamyTest.class.getResource("antisamy-ebay-1.4.4.xml");
            Policy policy = Policy.getInstance(configXmlUrl.getFile());
            
            AntiSamy as = new AntiSamy();
            CleanResults cleanResult = as.scan(concatString(lines), policy);

            System.out.println(cleanResult.getCleanHTML());
        } catch (IOException | PolicyException | ScanException ex) {
            throw new RuntimeException(ex);
        }
    }
 
    private static String concatString(List&amp;lt;String&amp;gt; input) {
        StringBuilder output = new StringBuilder();
        for(String line : input) output.append(line);
        return output.toString();
    }
}&lt;/pre&gt;A dirtyHtml.html fájl tartalma szabvány HTML (HTML, HEAD, BODY). A programot futtatva láthatjuk, hogy a body tartalmán kívül minden HTML taget kidobott az AntiSamy. Azért mondom, hogy minden HTML taget, mert ha van pl. title a headbe, a tartalma bizony ottmarad, tehát csak a sallang kerül ki. Szerintem ez utóbbi működés konfigurálható (fixme).&lt;/p&gt;&lt;p&gt;Az elméleti ismerkedés után ideje valami komolyabb megbizatást adni Samykének. Személy szerint Liferay fejlesztő vagyok, így szinte evidens, hogy erre esett a választásom. A Liferay 6-os verziója óta létezik egy Sanitizers-nek nevezett funkcionalitás, amely, bár még nem teljeskörű, mégis segít az igényes programozónak, a kritikus user inputokat szűrni. A funkcionalitás mint írtam nem teljeskörű, ugyanis egyelőre csak a Blog bejegyzéseket tudjuk kontrollálni out-of-the-box. A dolgunk egyszerű, a portal.properties-ben van egy sanitizer.impl paraméter, amit a portal-ext.properties-ben felül tudunk definiálni. Az alapbeállítás a com.liferay.portal.sanitizer.DummySanitizerImpl osztályra mutat, ami jóformán semmit nem csinál, viszont jó kiindulási pont lehet saját Saniterünk elkészítéséhez. Létezik a Liferaynek beépített osztálya com.liferay.portal.kernel.sanitizer.SanitizerUtil képében, választhatjuk ezt is, de saját megoldást is minden további nélkül.&lt;/p&gt;
&lt;p&gt;Miután Blog bejegyzéseinket megvédtük a sokszor óvatlan bloggerektől, sajnos nem dőlhetünk hátra nyugodtan, mivel a Liferayben is, mint minden CMS-ben, nem csak Blogot szerkesztenek a felhasználók, hanem számtalan egyéb módon is lehetőségük van HTML szöveget a rendszerbe juttatni. Mivel "gyári" támogatás még nincs ezen bejegyzésekre, nincs más lehetőségünk, mint hook-ot írni. A Plugin tárolóban van egy "antisamy hook", amely alapján könnyedén megírhatjuk saját kiterjesztésünket. A legjobb módszer un. &lt;a href="http://www.liferay.com/community/wiki/-/wiki/Main/Wrapper+Plugins"&gt;Model Wrapper Hook&lt;/a&gt; készítése. Hozzunk létre egy hook-ot, majd a liferay-hook.xml fájlba írjuk be a felülírandó szervíz definícióját
&lt;pre class="brush: xml;"&gt;
&amp;lt;hook&amp;gt;
    &amp;lt;service&amp;gt;
        &amp;lt;service-type&amp;gt;com.liferay.portlet.wiki.service.WikiPageLocalService&amp;lt;/service-type&amp;gt;
        &amp;lt;service-impl&amp;gt;com.test.hooks.SaniterWikiPageLocalService&amp;lt;/service-impl&amp;gt;
    &amp;lt;/service&amp;gt;
&amp;lt;/hook&amp;gt;
&lt;/pre&gt;
Majd írjuk meg saját osztályjunkat
&lt;pre class="brush: java;"&gt;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.sanitizer.SanitizerUtil;
import com.liferay.portal.kernel.util.ContentTypes;
import com.liferay.portal.service.ServiceContext;
import com.liferay.portlet.wiki.model.WikiPage;
import com.liferay.portlet.wiki.service.WikiPageLocalService;
import com.liferay.portlet.wiki.service.WikiPageLocalServiceWrapper;

public class SaniterWikiPageLocalService extends WikiPageLocalServiceWrapper {

    public SaniterWikiPageLocalService(WikiPageLocalService wikiPageLocalService) {
        super(wikiPageLocalService);
    }
 
    public WikiPage addPage(
            long userId, long nodeId, String title, double version,
            String content, String summary, boolean minorEdit, String format,
            boolean head, String parentTitle, String redirectTitle,
            ServiceContext serviceContext)
            throws PortalException, SystemException {

        String sanitizedContent = SanitizerUtil.sanitize(
            serviceContext.getCompanyId(), serviceContext.getScopeGroupId(),
            userId, WikiPage.class.getName(), 0, ContentTypes.TEXT_HTML, content);

        return super.addPage(userId, nodeId, title, version,
            sanitizedContent, summary, minorEdit, format,
            head, parentTitle, redirectTitle,
            serviceContext);
    }
}
&lt;/pre&gt;
Bár én a Wiki-t választottam példának, ez alapján bármely más szervízre megírható a szűrés.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-1796117221397261845?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/1796117221397261845/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2011/10/owasp-antisamy-javaban.html#comment-form' title='0 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/1796117221397261845'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/1796117221397261845'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2011/10/owasp-antisamy-javaban.html' title='OWASP AntiSamy Javaban'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-8837507174427251795</id><published>2011-09-24T15:35:00.000+02:00</published><updated>2011-09-24T15:35:39.536+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSGi'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Liferay'/><title type='text'>Liferay Portál OSGi bundle támogatással</title><content type='html'>&lt;p&gt;Liferay és OSGi témák már többször is előkerültek, de most egy olyan egyedülálló kombináció bemutatását tűztem ki célul, mely egyesíti a két platform minden előnyét. A bemutatott módszer aktív fejlesztés alatt áll, éles használatát nem ajánlom senkinek. A fejlesztés &lt;a href="http://www.liferay.com/web/raymond.auge/profile"&gt;Raymond Augé&lt;/a&gt; keze alatt történik, jelenleg a teljes kódbázis branch repóból érhető el. A cikk megírását az érdeklődés ihlette, és a remény, hogy másoknak is megtetszik a kezdeményezés, és programozók tucatjai állnak be Ray mögé, hogy minél előbb core feature legyen a dolog.&lt;/p&gt;&lt;p&gt;Első lépésként szükségünk lesz a módosított Liferay forrására, amit &lt;a href="https://github.com/rotty3000/liferay-portal/tree/OSGi"&gt;itt&lt;/a&gt; tudunk beszerezni. A letöltött forrást egyszerűen tömörítsük ki valahová. A fordításhoz egy &lt;a href="http://ant.apache.org/"&gt;Apache Ant&lt;/a&gt;ot is be kell üzemelnünk, ennek lépéseit nem részletezném. A kibontott Liferay gyökérében találunk egy app.server.properties állományt, melyben tudjuk konfigurálni, hogy milyen konténert szeretnénk használni. Amennyiben valami "különleges" igény nem szól közbe, én az &lt;a href="http://tomcat.apache.org/"&gt;Apache Tomcat&lt;/a&gt; verziót preferálom, ez egyébként az alapértelmezett. A properties fájlban &lt;a href="http://tomcat.apache.org/download-70.cgi"&gt;7.0.21&lt;/a&gt;-es verzió van beállítva, letöltés után a Liferay forrásunkkal párhuzamosan lévő bundles könyvtárba csomagoljuk is ki. Vagy az app.server.properties-ben írjuk át az útvonalat, vagy az apache-tomcat-7.0.21 könyvtárat nevezzük át tomcat-7.0.21-re.&lt;/p&gt;&lt;p&gt;A fordításhoz Antunkat is meg kell kicsit piszkálni. Először is az ecj.jar-t be kell másolni az Ant libjei közé (vagy szimlinkelni, vagy egyéb úton a classpath-ra tenni). Az ecj.jar beszerezhető a Plugin SDK lib könyvtárából. Be kell állítanunk az Ant memóriahasználatát is az alábbi módon.&lt;br /&gt;
&lt;pre class="brush: bash;"&gt;ANT_OPTS="-Xmx1024m -XX:MaxPermSize=256m"
export ANT_OPTS
&lt;/pre&gt;Nincs más hátra, mint elindítani a fordítást a Liferay gyökérkönyvtárában.&lt;br /&gt;
&lt;pre class="brush: bash;"&gt;ant all
&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;A fordítás befejeztével a ../bundles/tomcat-7.0.21/bin/startup.sh parancs futtatásával kelthetjük életre friss Liferayünket. Miután elindult, a Control Panel Server szekcióban találunk egy OSGi Admin menüpontot. Belépve láthatjuk, ha minden jól ment, hogy van egy OSGi System Bundle nevű batyu, ami aktív, tehát az OSGi konténer fut rendben.Az általam próbált verzióba Eclipse Equinox 3.7 volt integrálva, Ray elmondása szerint minden &gt;=4.1-es implementációval változtatás nélkül működik.&lt;/p&gt;&lt;p&gt;Amit a rendszer tud jelenlegi állás szerint:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Az adminisztrátor portleten keresztül lehetséges OSGi batyuk hozzáadása, eltávolítása, frissítése, elindítása, újraindítása, és leállítása.&lt;/li&gt;
&lt;li&gt;Egy saját deploy listenerrel a deploy könyvtárba másolt batyukat a Liferay megpróbálja telepíteni és elindítani.&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;Most, hogy működik a portálunk, nincs más dolgunk, mint nekiállni első OSGi Servletünk megalkotásához. A batyu készítésről &lt;a href="http://jpattern.blogspot.com/2010/07/osgi-szarnyprobalgatas.html"&gt;itt&lt;/a&gt; írtam részletesen, így az alapokba nem is mennék bele. Hozzunk létre egy OSGi Plugin Projectet. A MANIFEST.MF állomány tartalma legyen a következő:&lt;br /&gt;
&lt;pre&gt;Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: OSGiOnLiferay
Bundle-SymbolicName: OSGiOnLiferay;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: javax.servlet;version="2.5.0",
 javax.servlet.http;version="2.5.0",
 org.eclipse.equinox.http;resolution:=optional,
 org.osgi.framework;version="1.6.0"

&lt;/pre&gt;Hozzunk létre a gyökér-könyvtárban egy plugin.xml-t, amelybe regisztráljuk a Servletünket.&lt;br /&gt;
&lt;pre class="brush: xml;"&gt;&lt;plugin&gt;
   &lt;extension
         point="org.eclipse.equinox.http.registry.servlets"
         alias="/osgionliferay"
         class="com.blogspot.jpattern.osgionliferay.TestServlet"&gt;
   &lt;/extension&gt;
&lt;/plugin&gt;
&lt;/pre&gt;Végezetül írjunk egy egyszerű Servletet.&lt;br /&gt;
&lt;pre class="brush: java;"&gt;public class TestServlet extends HttpServlet {
 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
   throws ServletException, IOException {
  PrintWriter out = resp.getWriter();
  out.print("Java'nother blog");
  out.close();
 }
}
&lt;/pre&gt;A plugin buildelése után egyszerűen másoljuk be a kapott JAR-t a deploy könyvtárba. A Liferay logot nézve az alábbi sor jelöli a sikeres telepítést.&lt;br /&gt;
&lt;pre&gt;12:14:29,798 INFO  [AutoDeployDir:167] Processing sample.jar&lt;/pre&gt;Az admin felületre látogatva remélhetőleg meg is találjuk batyunkat az OSGi Admin felületen, nincs más dolgunk, mint aktiválni.&lt;/p&gt;&lt;p&gt;És itt jön a szomorú, ám reményteli vég. Mint említettem eléggé fejlesztési fázisban van a projekt, ami sajnálatosan azt is jelenti, hogy ezen a ponton nem tudunk túljutni. Ray a közeljövőben publikál egy birdge Servletet, aminek a segítségével a Liferay a HTTP kéréseket az OSGi HttpService rétegén keresztül bármely, a keretrendszerbe regisztrált Servletnek vagy JSPnek továbbítani tudja. Tervezi a Plugin SDK bővítését is, hogy OSGi batyukat lehessen vele kényelmesen fejleszteni. Véleményem szerint ígéretes a kezdeményezés, hiszen a Liferay egy jól gyúrható platform, az OSGi, pedig égy végtelenül rugalmas keretrendszer.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-8837507174427251795?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/8837507174427251795/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2011/09/liferay-portal-osgi-bundle-tamogatassal.html#comment-form' title='1 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/8837507174427251795'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/8837507174427251795'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2011/09/liferay-portal-osgi-bundle-tamogatassal.html' title='Liferay Portál OSGi bundle támogatással'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-209403327119630229</id><published>2011-08-27T11:24:00.002+02:00</published><updated>2011-08-27T11:31:08.791+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Validálás, ahogy én csinálom</title><content type='html'>&lt;p&gt;Egy programozó életében rendkívül gyakran előfordul, hogy entitásokat kell validálnia. Lényegében az üzleti logika mindig úgy kezdődik, hogy a bemeneti paramétereket, melyek legtöbbször felhasználói interakció által kerülnek be, validálni kell, hogy a hibás entitások a későbbiekben ne okozzanak hibát a program működésébe. A validálás az a dolog, amit egy kicsit mindenki másként csinál, mindenkinek megvan a saját módszere. Ebben az írásban bemutatnám, én hogyan is szoktam ezt a problémát orvosolni. A bemutatott megoldás nem tökéletes, de remélem gondolat-ébresztőnek megteszi.&lt;/p&gt;&lt;p&gt;A validálás alapproblémája szerintem a következő: az entitások validitását az üzleti réteg végzi el, amelyhez különböző kliensek kapcsolódnak. A különböző kliensek különböző nyelveken, és módon kívánják megjeleníteni a hibaüzeneteket, tehát az üzleti rétegbe írt return "Hibás a név!" és hasonlók megengedhetetlenek (később úgyis kiderül, hogy többnyelvű lesz a megjelenítés). A következő probléma, amivel szembesülünk, hogy egyszerre több hibát is tudni kell kezelni, hiszen egy bonyolult form kitöltésekor nem szerencsés egyesével untatni a felhasználót a hibák közlésével, meg egyébként sem tudjuk klienseink miként működnek. A nyelvi problémát orvosolni viszonylag egyszerű, vezessünk be jól dokumentált hibakódokat, klienseink, meg feldolgozzák saját belátásuk szerint. Az egyidejű több hibajelzés kérdésköre már egy kicsit rafináltabb dolog. A legegyszerűbb megoldás, egy hibakódokkal feltöltött tömb, vagy Collection visszaadása. Ezzel a megoldással a legnagyobb problémám, hogy rengeteg favágó programozással jár. Ha mindenhová, mind kliensben mind az üzleti rétegben kézzel írogatjuk a hibakódokat, akkor a hibaszázalékunk igencsak megnő, mert óhatatlanul elgépeljük valahol a kódot. Ha bevezetünk konstansokat, vagy Enumokat, akkor legalább a hibákat kiküszöböljük, de a ThisIsMyEntity.ERROR_NAME_REQ gépelésébe fogunk megőszülni.&lt;br /&gt;
&lt;/p&gt;&lt;p&gt;Nekem a következő megoldás vált be: mivel 2db kettő hatványa sosem lesz egy harmadik kettő hatványával egyenlő, a hibakódot adjuk vissza kettő hatványaival. Tehát, ha hiányzik a név adjunk vissza 2-t, amennyiben a lakcím hiányzik adjunk vissza 4-et, ha pedig mindkettő hiányzik adjunk vissza 6-ot. A metódus implementációját személy szerint magába az Entitást megvalósító osztályba szoktam tenni, de ez ízlés dolga. Van aki szerint az Entitásnak csak egy &lt;a href="http://en.wikipedia.org/wiki/Data_transfer_object"&gt;DTO&lt;/a&gt;-nak kell lennie, és a validitás eldöntése az üzleti logika feladata. Véleményem szerint egy entitás tudja leginkább magáról eldönteni, hogy megfelelőek-e a benne foglalt adatok.&lt;br /&gt;
&lt;pre class="brush: java"&gt;public int validate() {
            int valid = 0;
            
            if (foo == null || foo.isEmpty()) valid |= 1 &amp;lt;&amp;lt; 1;
            if (bar == null) valid |= 1 &amp;lt;&amp;lt; 2;
            if (param == null || param.isEmpty()) valid |= 1 &amp;lt;&amp;lt; 3;
            
            return valid;
        }
&lt;/pre&gt;Mivel későbbiekben is bitműveletekkel fogunk operálni, elegánsabbnak tartottam bitműveletekkel megoldani az összeadást, természetesen dolgozhatunk "hagyományos" számokkal is.&lt;br /&gt;
&lt;/p&gt;&lt;p&gt;Kliens oldalon a hibakód lekezelése pofon egyszerű. Kiválasztunk egy tetszőleges 2 hatványt (érdemes a dokumentációban is említettet választani :)), mondjuk a 4-et, és ellenőrizzük, hogy adott bit, 2^2 az 100, tehát a harmadik, egyezik-e a visszakapott érték harmadik bitjével.&lt;br /&gt;
&lt;pre class="brush: java;"&gt;boolean somethingIsValid = (4 &amp; entity.validate()) == 4;
&lt;/pre&gt;Ez a módszer sem tökéletes, hiszen a hibákat csak bővíteni lehet, egyéb változtatás esetén már meglévő klienseink fognak hibásan működni. A teljes példaprogram az alábbi:&lt;br /&gt;
&lt;pre class="brush: java;"&gt;public class ValidatorTest {

    private static final Map&amp;lt;Integer, String&amp;gt; ERRORS = new HashMap&amp;lt;Integer, String&amp;gt;();

    static {
        ERRORS.put(2, "Foo is required!");
        ERRORS.put(4, "Bar is required!");
        ERRORS.put(8, "Param is required!");
    }

    public static void main(String[] args) {
        List&amp;lt;Entity&amp;gt; entities = Arrays.asList(new Entity[] {
            new Entity(null, null, null),
            new Entity("foo", null, null),
            new Entity("", null, "param")
        });

        for(Entity entity : entities) {
            System.out.println(entity);
            int valid = entity.validate();
            
            for(Integer code : ERRORS.keySet()) {
                if ((code &amp; valid) == code) {
                    System.out.println(ERRORS.get(code));
                }
            }
        }
    }

    static class Entity {

        private String foo;
        private Integer bar;
        private String param;
        
        public Entity(String foo, Integer bar, String param) {
            this.foo = foo;
            this.bar = bar;
            this.param = param;
        }

        public int validate() {
            int valid = 0;
            
            if (foo == null || foo.isEmpty()) valid |= 1 &amp;lt;&amp;lt; 1;
            if (bar == null) valid |= 1 &amp;lt;&amp;lt; 2;
            if (param == null || param.isEmpty()) valid |= 1 &amp;lt;&amp;lt; 3;
            
            return valid;
        }
    }
}
&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;Nagyon kíváncsi vagyok a véleményetekre, illetve, hogy nektek milyen módszer vált be ebben a témakörbe.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-209403327119630229?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/209403327119630229/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2011/08/validalas-ahogy-en-csinalom.html#comment-form' title='3 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/209403327119630229'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/209403327119630229'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2011/08/validalas-ahogy-en-csinalom.html' title='Validálás, ahogy én csinálom'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-1406109422999352157</id><published>2011-08-15T08:54:00.001+02:00</published><updated>2011-08-27T11:34:15.162+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Collection'/><title type='text'>Collections.emptyList() és a generikus típuskényszerítés</title><content type='html'>Sajnos mostanában nem jut annyi időm blogolásra, mint szeretném, és ez a bejegyzés is mindössze egy szösszenet lesz.&lt;br /&gt;
A Collections osztály, mint nevéből is következik, a &lt;a href="http://download.oracle.com/javase/6/docs/api/java/util/Collection.html"&gt;Collection&lt;/a&gt; példányok manipulálására, és előre definiált Collection-ök hozzáféréséhez nyújt kényelmes eszközt. Az osztály részleteibe nem szeretnék belemenni, ezt az olvasóra bízom. Kiszemelt áldozatom az emptyList metódus. A metódus forrását megnézve láthatjuk, hogy nem csinál egyebet, mint az osztályban statikus paraméterként deklarált nyers típusú EMPTY_LIST-et adja vissza a kívánt típusra alakítva. Természetesen ez a lista egy változtathatatlan lista, tehát elemeket ebbe nem tudunk tenni. Használata végtelenül egyszerű.&lt;br /&gt;
&lt;pre class="brush: java"&gt;List&amp;lt;String&amp;gt; stringList = Collections.emptyList();&lt;/pre&gt;A fordító megpróbálja kitalálni, hogy milyen típusra kell alakítani az EMPTY_LIST példányt, teszi ezt úgy, hogy megnézi milyen típusba szeretnénk belepasszírozni a listát. Az esetek többségében sikerül is ez a művelet, ám mit tehetünk a következő esetben:&lt;br /&gt;
&lt;pre class="brush: java"&gt;public static void bar() {
        foo(Collections.emptyList());
    }
    
    public static void foo(List&amp;lt;String&amp;gt; stringList) {}
&lt;/pre&gt;Már az IDE is jelzi, hogy ez így nem lesz jó, nem tudja megállapítani milyen típusra kell alakítani a listát. Netbeans fel is ajánlja, hogy létrehoz egy foo(List&amp;lt;Object&amp;gt; stringList) metódust, de mi ezt nem szeretnénk. Egyik lehetőségünk, hogy a paramétert egy lokális változóba kimozgatjuk, és a helyes típussal adjuk át a metódusnak. Ez a megoldás célra vezető, viszont feleslegesen növeli kódunk méretét. A következő eshetőség, hogy a nyers EMPTY_LIST-et adjuk át paraméterként, viszont így hazavágjuk a típusbiztonságot. Nyers típusoknál a JVM egyszerűen levágja a típusellenőrzést. Ennek oka a visszafelé kompatibilitás, a generikusok megjelenése óta nem javallott a nyers típusok használata. Az elegáns megoldás egyszerű, segítsük a fordítót, és mondjuk meg neki milyen típusra alakítsa a listát.&lt;br /&gt;
&lt;pre class="brush: java;"&gt;foo(Collections.&amp;lt;String&amp;gt;emptyList());
&lt;/pre&gt;Az itt említett módszer nem a Collections osztály sajátossága, alkalmazható minden generikus metódusra. Választásom azért esett erre az osztályra, mert ezt eléggé gyakran használja mindenki , aki Java-ban programozik, és része a Java SE-nek.&lt;br /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-1406109422999352157?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/1406109422999352157/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2011/08/collectionsemptylist-es-generikus.html#comment-form' title='2 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/1406109422999352157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/1406109422999352157'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2011/08/collectionsemptylist-es-generikus.html' title='Collections.emptyList() és a generikus típuskényszerítés'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-8385374680823418741</id><published>2011-04-28T10:31:00.001+02:00</published><updated>2011-04-28T10:31:45.157+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>JDK7 újdonság: java.util.Objects</title><content type='html'>A java.util.Arrays és java.util.Collections mintájára a Java következő verziója elhozza számunkra a java.util.Objects "eszközt". Lényegében semmi újdonság nincs az osztályban, egyszerűen olyan kényelmi funkciókat tartalmaz, melyek megkönnyítik a fejlesztők életét. Az objektumnak mindössze 9 statikus metódusa van, melyek az alábbiak:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;compare(T a, T b, Comparator c):int - Mint a neve is mutatja objektumokat tudunk vele összehasonlítani, az egyetlen érdekessége, hogy Objects.compare(null, null) visszatérési értéke 0&lt;/li&gt;
&lt;li&gt;equals(Object a, Object b):boolean - Szintén zenész Objects.equals(null, null) igaz értékkel tér vissza&lt;/li&gt;
&lt;li&gt;deepEquals(Object a, Object b):boolean - Igazzal tér vissza, ha a két objektum "mélységében" egyenlő, minden más esetben hamis. Szintén elmondható, hogy Objects.deepEquals(null, null) igaz. Amennyiben mindkét átadott paraméter tömb, a metódus meghívja az Arrays.deepEquals-t ellenkező esetben az első objektum equals metódusának adja paraméterül a második paramétert&lt;/li&gt;
&lt;li&gt;hashCode(Object o):int - Visszatér az átadott objektum hashCode-jával, null paraméter esetén 0-val&lt;/li&gt;
&lt;li&gt;hash(Object... values):int - Az Arrays.hashCode(Object[]) mintájára az átadott paramétereknek kiszámolja a hashCode-ját. Gyakorlati haszna leginkább a DTO osztályoknál mutatkozik meg, ahol több objektum összessége alkot egy egységet.&lt;br /&gt;
&lt;pre class="brush: java;"&gt;public class Student {
    private Integer id;
    private Mother mother;
    private Father father;
    ...
    @Override public int hashCode() {
        return Objects.hash(id, mother, father);
    }
}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;requireNonNull(T obj):T - Ez a metódus egy egyszerű validáció, amely NullPointerException-t dob, amennyiben az átadott referencia értéke null. A metódusnak átadhatunk még egy String paramétert, ekkor ez lesz a kivétel szövege&lt;/li&gt;
&lt;li&gt;toString(Object o): String - Az átadott objektum toString metódusával tér vissza, null érték esetén "null" Stringgel. Második paraméternek egy String is átadható, ez a paraméter lesz a null érték toString-je&lt;br /&gt;
&lt;pre class="brush: java;"&gt;logger.info(Objects.toString(bar, “Bar is null”));
&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;Kettős érzelmekkel viseltetek az "újjítás" iránt. Egyrészt némi iróniával kijelenthető, hogy most aztán kitettek magukért a srácok, nem hiába csúszik a kiadás. Másfelöl viszont elég sok felesleges kódolástól szabadít meg az eszköz, és mivel része lesz a JDK-nak egy "szabványos" megoldást kínál hétköznapi problémákra.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-8385374680823418741?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/8385374680823418741/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2011/04/jdk7-ujdonsag-javautilobjects.html#comment-form' title='0 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/8385374680823418741'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/8385374680823418741'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2011/04/jdk7-ujdonsag-javautilobjects.html' title='JDK7 újdonság: java.util.Objects'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-723961752829187921</id><published>2011-04-01T15:12:00.003+02:00</published><updated>2011-04-01T15:13:43.061+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Liferay'/><title type='text'>Liferay "gyári" Servicek és a DynamicQuery használata</title><content type='html'>Egy előző &lt;a href="http://jpattern.blogspot.com/2010/11/liferay-servide-builder.html"&gt;bejegyzésben&lt;/a&gt; foglalkoztam már a Service Builderrel, amivel saját szolgáltatásokat illeszthetünk a Liferay (továbbiakban LR) portleteinkbe. A LR természetesen a ServiceBuildert nem csak külső integrációhoz használja, hanem saját adatbázis-rétegének eléréséhez is. A szolgáltatások közül leginkább a ...LocalServiceUtil osztályokra lesz szükségünk. Ezen osztályok használatára nem szeretnék kitérni, statikus metódusaik lekérdezésével "egyértelmű", hogy melyik mire való. Amennyiben a "gyári" metódusokat használjuk legtöbb esetben sajnos számolnunk kell a LR sajátosságaival, szerencsére azonban van lehetőségünk saját SQL futtatására is, természetesen ebben az esetben nem ússzuk meg az adatbázis szerkezetének megismerését. A saját SQL-ek rendszerbe juttatásáról a &lt;a href="http://www.liferay.com/community/wiki/-/wiki/Main/Queries+2%3A+DynamicQuery+API"&gt;DynamicQuery&lt;/a&gt; osztály gondoskodik. Ennek használata nem túl bonyolult, de mitől is lenne az.&lt;br /&gt;
&lt;pre class="brush: java;"&gt;DynamicQuery query = DynamicQueryFactoryUtil
    .forClass(User.class, PortalClassLoaderUtil.getClassLoader());
query.add(PropertyFactoryUtil.forName("email").eq("foo@bar.hu"));
query.addOrder(OrderFactoryUtil.asc("name"));
List fullResult = UserLocalServiceUtil.dynamicQuery(query);
List someResult = UserLocalServiceUtil.dynamicQuery(query, start, end);
long resultsNo = UserLocalServiceUtil.dynamicQueryCount(query);
&lt;/pre&gt;Fontos tudnunk, hogy LR osztályok 2 fő csoportba sorolhatók. Az egyik a portal-impl, amely kívülről nem érhető el, ezek a LR saját belső osztályai, és a portal-service, amik pedig a kifelé ajánlott szolgáltatások. Sajnos nem sikerült a szétválasztást tökéletesen megvalósítani, legalábbis szerintem, így ha a PortalClassLoaderUtil.getClassLoader() paramétert nem adjuk meg a DynamicQueryFactoryUtilnak, akkora ClassNotFoundException-t kapunk mint a ház :). Ezt az egy "apró" részletet leszámítva viszonylag jól dokumentált a DynamicQuery használata, és az alapvető műveletekre elégséges.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-723961752829187921?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/723961752829187921/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2011/04/liferay-gyari-servicek-es-dynamicquery.html#comment-form' title='0 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/723961752829187921'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/723961752829187921'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2011/04/liferay-gyari-servicek-es-dynamicquery.html' title='Liferay &quot;gyári&quot; Servicek és a DynamicQuery használata'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-9007316367899449025</id><published>2011-01-29T22:06:00.001+01:00</published><updated>2011-01-29T22:12:03.354+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Portlet'/><category scheme='http://www.blogger.com/atom/ns#' term='Liferay'/><title type='text'>Liferay FriendyUrlMapping vs. IPC</title><content type='html'>Amikor először futottam bele a címben nevezett probléma-együttesbe, még azt gondoltam, hogy csak egyedi az eset, így a megolldást nem jegyeztem meg, és sajnos le sem. Viszont mikor másodjára is szembetalálkoztam vele, és újra végigjártam a szamárlétrát, gondoltam megkímélem az emberiséget, vagy legalább az olvasóimat ezen gyötrelmektől.&lt;br /&gt;
A portletek világában sajnos állandó problémát jelent a felhasználóbarát URL-ek kezelése. Ennek oka, hogy egy portletnek két fázisa létezik, az egyik a render-fázis, a másik pedig az action-fázis. Álltalános eset, hogy amíg egy portlet action-fázisban van, addig a többi render-fázisba. A böngészőből átvitt adatoknak a szerver felé tartalmazniuk kell valamiféle hivatkozást az adott portlet-példányról, hogy a portlet-konténer el tudja dönteni kinek is címezték a kérést. Paraméterben szokott szerepelni a portlet nézet beállítása (minimalizált, normál, maximalizált, exclusive, ez utóbbi liferay specifikus), valamint az életciklusra, és portlet módra (view|edit|etc) vonatkozó adat. Ennek eredménye valami ehhez hasonló URN: ?p_p_id=searchaction_WAR_fakeportletshoppingportlet&amp;amp;p_p_lifecycle=1&amp;amp;p_p_state=normal&amp;amp;p_p_mode=view&amp;amp;p_p_col_id=column-2&amp;amp;p_p_col_count=1&amp;amp;_searchaction_WAR_fakeportletshoppingportlet_javax.portlet.action=search. A probléma megoldására a Liferay többféle megoldást is kínál, de erről később. A probléma másik része, hogy a portlet-szabvány második verziója óta lehetőség van portletek közötti kommunikációra, röviden IPC-re. Az IPC lényege tömören, hogy az egyik portletünk kivált, publikál egy eseményt, míg egy vagy több másik portlet, amelyek előzőleg feliratkoztak az eseményre, elkapják azt. Az eseményben lehetőség van adatok átpasszíroázára is, így válik teljessé a kommunikáció. A dolog hátulütője, hogy kiváltani eseményt csak action fázisban lehet, és a kiváltott eseményt a másik portlet render fázisának egy bizonyos szakaszában képes elkapni, tehát érezhetően nem teljes a fejlesztői szabadság. Összegezve a problémát egy friendly url-en (továbbiakban FU) "figyelő" portletnek kell jeleznie állapotáról egy másik portletnek.&lt;br /&gt;
&lt;h3&gt;IPC kialakítása&lt;/h3&gt;A megvalósításhoz nincs más dolgunk, mint bejegyezni a portlet.xml-ben egy globális eseményleírást, ezzel rögzítve magát az eseményt.&lt;br /&gt;
&lt;pre class="brush: xml;"&gt;&amp;lt;event-definition&amp;gt;
    &amp;lt;qname xmlns:x="http://jpattern.blogspot.hu/events"&amp;gt;x:ipc.myEvent&amp;lt;/qname&amp;gt;
    &amp;lt;value-type&amp;gt;java.lang.String&amp;lt;/value-type&amp;gt;
&amp;lt;/event-definition&amp;gt;
&lt;/pre&gt;Ezzel művelettel a http://jpattern.blogspot.hu/events névtéren bejegyeztünk egy ipc.myEvent eseményt.&lt;br /&gt;
Ezután mindkét portletünket szerepkörüknek megfelelően felvértezzük a kommunikációval.&lt;br /&gt;
&lt;pre class="brush: xml;"&gt;&amp;lt;supported-publishing-event&amp;gt;
    &amp;lt;qname xmlns:x="http://jpattern.blogspot.hu/events"&amp;gt;x:ipc.myEvent&amp;lt;/qname&amp;gt;
&amp;lt;/supported-publishing-event&amp;gt;

&amp;lt;supported-processing-event&amp;gt;
    &amp;lt;qname xmlns:x="http://jpattern.blogspot.hu/events"&amp;gt;x:ipc.myEvent&amp;lt;/qname&amp;gt;
&amp;lt;/supported-processing-event&amp;gt;
&lt;/pre&gt;Az esemény publikálása az alábbi módon történik:&lt;br /&gt;
&lt;pre class="brush: java;"&gt;QName qName = new QName("http://jpattern.blogspot.hu/events", "ipc.myEvent");
response.setEvent(qName, "param"); //ActionResponse
&lt;/pre&gt;Az esemény elkapásához portlet osztályunkban kell delegálnunk egy metódust:&lt;br /&gt;
&lt;pre class="brush: java;"&gt;@ProcessEvent(qname = "{http://jpattern.blogspot.hu/events}ipc.myEvent")
public void myEvent(EventRequest request, EventResponse response) {
    Event event = request.getEvent();
    String param = (String) event.getValue();
    response.setRenderParameter("param", param);
}
&lt;/pre&gt;Ezek után a JSP-ben nincs más dolgunk mint kiszedni a requestből a paramétert:&lt;br /&gt;
&lt;pre class="brush: java;"&gt;renderRequest.getParameter("param");
&lt;/pre&gt;Ennyi a varázslat, kipróbálásához házi feladat a &lt;a href="http://java.sun.com/portlet_2_0"&gt;portlet taglib&lt;/a&gt; actionUrl használata.&lt;br /&gt;
&lt;h3&gt;A FriendlyUrlMapping&lt;/h3&gt;Az említett több lehetőség közül nekem szimpatikusabb volt az XML szerkesztgetésnél, hogy létrehozok egy osztályt, amely beállítja a portletet az URL alapján. Az osztállyal szemben támasztott követelmény, hogy a com.liferay.portal.kernel.portlet.BaseFriendlyURLMapper osztály leszármazottja legyen.&lt;br /&gt;
&lt;pre class="brush: java;"&gt;public class MyFriendlyUrlMapper extends BaseFriendlyURLMapper {

private static final String MAPPER = "mappingstring"; //erre az URL darabra fog aktiválódni a mapper
 
@Override
public void populateParams(String friendlyURLPath,
        Map&amp;lt;String, String[]&amp;gt; params, Map&amp;lt;String, Object&amp;gt; requestContext) {
    addParameter(params, "p_p_id", getPortletId());
    addParameter(params, "p_p_lifecycle", "1"); //ez a paraméter kényszeríti action-fázisba a portletet
    addParameter(params, "p_p_mode", PortletMode.VIEW);
    addParameter(params, "p_p_state", WindowState.NORMAL);
    addParameter(getNamespace(), params, "javax.portlet.action", "actionMethod"); //Ez az action-metódus fog meghívódni
 }
 
@Override
public boolean isCheckMappingWithPrefix() {
    return false;
}
 
@Override
public String getMapping() {
    return MAPPER;
}
 
@Override
public String buildPath(LiferayPortletURL portletURL) {
    return null;
}

}
&lt;/pre&gt;Miután elkészítettük az osztályt, be kell jegyeznünk a liferay-portlet.xml fájlban a megfelelő portletnél azt:&lt;br /&gt;
&lt;pre class="brush: xml;"&gt;&amp;lt;friendly-url-mapper-class&amp;gt;com.blogger.jpattern.MyFriendlyUrlMapper&amp;lt;/friendly-url-mapper-class&amp;gt;
&lt;/pre&gt;Érdemes megjegyezni, hogy a FU kezelés a LR-ben úgy működik, hogy felveszünk egy oldalt, pl.: /oldal, amire kihelyezzük a portletet, és a LR /oldal/mappingstring URL esetén aktiválja a Mapper osztályt.&lt;br /&gt;
Amennyiben a portletünket render fázisban szeretnénk használni p_p_lifecycle=0, nincs más dolgunk, de mint említettem IPC esemény kiváltására csak action-fázisban van lehetőség. A LR-ben ki kell kapcsolnunk az "Auth token check"-et, ami annyit tesz, hogy minden action típusú kéréshez a LR generál egy egyedi azonosítót, és ennek hiányában nem enged hozzáférést az erőforráshoz. Nyilván FU használata esetén nem rendelkezünk ezzel az azonosítóval, ezért a portal-ext.properties fájlban vegyük fel a "auth.token.check.enabled=false" paramétert.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-9007316367899449025?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/9007316367899449025/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2011/01/liferay-friendyurlmapping-vs-ipc.html#comment-form' title='2 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/9007316367899449025'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/9007316367899449025'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2011/01/liferay-friendyurlmapping-vs-ipc.html' title='Liferay FriendyUrlMapping vs. IPC'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-3200156270691323994</id><published>2010-11-28T15:25:00.012+01:00</published><updated>2011-04-01T07:00:19.968+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Liferay'/><title type='text'>Liferay Service Builder</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.liferay.com/c/wiki/get_page_attachment?p_l_id=4464764&amp;amp;nodeId=1071674&amp;amp;title=Service+Builder&amp;amp;fileName=SharedImages%2fmodel-service-persistence.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;" target="_blank"&gt;&lt;img border="0" height="200" src="http://www.liferay.com/c/wiki/get_page_attachment?p_l_id=4464764&amp;amp;nodeId=1071674&amp;amp;title=Service+Builder&amp;amp;fileName=SharedImages%2fmodel-service-persistence.jpg" width="137" /&gt;&lt;/a&gt;&lt;/div&gt;A Liferay az egyedi portletek fejlesztését saját MVC keretrendszerével kívánja élvezetesebbé tenni, az un. Liferay MVC-vel. A keretrendszer legizgalmasabb része a Model-t előállító Service Builder, ennek bemutatására teszek most egy kísérletet. Felépítését tekintve az adatbázis réteg fölött &lt;a href="http://en.wikipedia.org/wiki/Hibernate_%28Java%29"&gt;Hibernate&lt;/a&gt; biztosítja az entitások adatbázissal való megfeleltetését. A Hibernatet a Liferay saját perzisztencia rétege burkolja be. A perzisztencia réteggel a Servicek tartják a kapcsolatot, melyek lehetnek lokálisak és távoliak egyaránt, és általuk szerezhetünk referenciát Entitásainkra, melyeket a Liferay analógia szerint Modeleknek hívunk. A könnyebb megértést segíti a mellékelt ábra.&lt;br /&gt;
Első lépésként, ha még nem tettük volna, hozzunk létre egy New -&amp;gt; Liferay Plug-in Projectet kedvenc Eclipse fejlesztői környezetünkben. Eclipshez létezik egy &lt;a href="http://www.liferay.com/community/wiki/-/wiki/Main/Liferay+IDE"&gt;Liferay IDE&lt;/a&gt; nevű plugin, ami azért nagyban megkönnyíti a fejlesztést, természetesen a &lt;a href="http://www.liferay.com/community/wiki/-/wiki/Main/Plugins+SDK"&gt;Liferay SDK&lt;/a&gt;-t is használhatjuk, én a pluginra támaszkodom. Következő dolog amit létre kell hoznunk, az egy New -&amp;gt; Liferay Service. A beállító-panelen állítsuk be a Plug-in Projectet, Package path paraméternek adjuk meg a service réteg ős-csomagjának nevét, ez tetszőleges, Namespace paraméternek a tábla prefixet tudjuk megadni, és végül saját nevünket. A Namespace paraméter üresen is hagyható jelentősége annyi, hogy mivel az érintett táblákat a Liferay&amp;nbsp; alap értelmezetten saját táblái mellé hozza létre könnyebb átlátni, ha prefixeljük a táblaneveket. Ha végeztünk találunk egy service.xml állományt a docroot/WEB-INF mappában. Dolgunk mindössze annyi, hogy XML formában rögzítsük Modelljeink struktúráját.&lt;br /&gt;
&lt;pre class="brush: xml;"&gt;&amp;lt;!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.0.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_0_0.dtd"&amp;gt;
&amp;lt;service-builder package-path="hu.jpattern.model"&amp;gt;
 &amp;lt;author&amp;gt;mhmxs&amp;lt;/author&amp;gt;
 &amp;lt;namespace&amp;gt;library&amp;lt;/namespace&amp;gt;
 &amp;lt;entity name="Book" local-service="true" remote-service="false"&amp;gt;
  &amp;lt;column name="bookId" type="long" primary="true" /&amp;gt;
  &amp;lt;column name="title" type="String" /&amp;gt;
  &amp;lt;column name="author" type="Collection" entity="Author" mapping-key="authorId" /&amp;gt;
  &amp;lt;column name="description" type="String" /&amp;gt;
  &amp;lt;column name="price" type="long" /&amp;gt;
  
  &amp;lt;column name="groupId" type="long" /&amp;gt;
  &amp;lt;column name="companyId" type="long" /&amp;gt;
  
  &amp;lt;order by="asd"&amp;gt;
   &amp;lt;order-column name="title" /&amp;gt;
   &amp;lt;order-column name="price" order-by="desc" /&amp;gt;
  &amp;lt;/order&amp;gt;
  
  &amp;lt;finder return-type="Collection" name="title"&amp;gt;
   &amp;lt;finder-column name="title" comparator="LIKE" /&amp;gt;
  &amp;lt;/finder&amp;gt;
 &amp;lt;/entity&amp;gt;
 &amp;lt;entity name="Author" local-service="true" remote-service="false"&amp;gt;
  &amp;lt;column name="authorId" type="long" primary="true" /&amp;gt;
  &amp;lt;column name="name" type="String" /&amp;gt;
  
  &amp;lt;column name="groupId" type="long" /&amp;gt;
  &amp;lt;column name="companyId" type="long" /&amp;gt;
 &amp;lt;/entity&amp;gt;
&amp;lt;/service-builder&amp;gt;&lt;/pre&gt;Az XMl-ben az entity tag írja le az entitásokat. Először is egy kötelező név attribútumot kell megadnunk, majd a szerviz hívására vonatkozóakat. A példában csak a lokális Servicekkel foglalkozok, de tudni érdemes, hogy létezik remote-service attribútum is, alapértelmezett true!!, mely &lt;a href="http://en.wikipedia.org/wiki/SOAP"&gt;SOAP&lt;/a&gt; szolgáltatásokat valósít meg a Liferayyel együtt szállított &lt;a href="http://en.wikipedia.org/wiki/Apache_Axis"&gt;Axison&lt;/a&gt; keresztül. Következnek a mezőleírások, melyek típusa primitív, egyszerű adat típus (String), vagy Date lehet. A primary="true" paraméterrel a PK-t határozhatjuk meg, értelemszerűen entitásonként legfeljebb 1-et. Lehetőségünk van entitások közötti kapcsolat létrehozására is, erre példa a Book author mezője, ahol a kapcsolat típusát, módját kell megadni, és az összekapcsolás kulcsát. A mapping-key OTM (@OneToMany) a mapping-table pedig MTM (@ManyToMany) kapcsolatot hoz létre entitásaink között. Liferay "poliszi", hogy nem használnak idegen kulcsokat az adatbázisban,  minden kapcsolatot Java kódból kell kezelni, ezért a Service Builder  sem támogatja ezt a dolgot. Személy szerint nekem ez roppant  unszimpatikus, hiszen rengeteg hibát rejt magában a koncepció, a  fejlesztők emberek, és óhatatlan, hogy bonyolultabb adat-struktúra  esetén valami mégis kimarad a kódból. Az egyetlen "elfogadható" érv a  Liferay döntése mellett, hogy így tudják biztosítani a minél szélesebb  adatbázis-paletta támogatását. Érdemes felvenni a groupId és companyId mezőket, mert a Liferay ezen mezők szerint fogja tudni összekötni entitásunkat saját jogosultság-kezelő rendszerével. A Service Builder lehetőséget ad egy rendezési elv meghatározására az order mező megadásával, példánkban cím szerint ASC, és ár szerint DESC rendezést valósítunk meg. Sajnos order-ből egyetlen egy szerepelhet.&amp;nbsp; Kereséseket könnyítendő meghatározhatunk előre generált kereső metódusokat a finder mezővel, ez eléggé hasznos, ugyanis tetszőleges számú lehet, és rögtön megadható a comperatorlt, =, !=, &amp;lt;, &amp;lt;=, &amp;gt;, &amp;gt;=,&amp;nbsp; és LIKE lehet, értelemszerűen a = az alapértelmezett.&amp;nbsp; Bár a példában nem szerepel, létezik egy tx-required mező, melynek szöveges tartalmában kell azon metódusokat felsorolni, amikre szeretnénk tranzakciókezelést használni. Ennek értéke alapértelmezetten add*, check*, clear*,&lt;br /&gt;
delete*, set*, és update*, így az alap Service metódusok le is vannak vele fedve. További lehetőségekről a &lt;a href="http://www.liferay.com/dtd/liferay-service-builder_6_0_0.dtd"&gt;liferay-service-builder_6_0_0.dtd&lt;/a&gt;ből tájékozódhatunk :). Végezetül a service.xml szerkesztőjében nyomjuk meg a Build services gombot, aminek hatására az IDE legenerálja a szükséges osztályokat.&lt;br /&gt;
A létrejött struktúrát megvizsgálva az alábbi osztályok "érdekesek" számunkra:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;hu.jpattern.model.service.base.xxxLocalServiceBaseImpl (xxx az adott entitás osztály) ez az osztály tartalmazza az alap szerviz metódusainkat. Ezt az osztályt ne módosítsuk!&lt;/li&gt;
&lt;li&gt;hu.jpattern.model.service.impl.xxxLocalServiceImpl osztályban tudjuk megvalósítani saját szerviz metódusainkat, melyek nincsenek benne a xxxLocalServiceBaseImpl osztályban. Fontos tudni, hogy az ebben az osztályba írt publikus metódusokat a Service Builder beleteszi az interfészbe következő buildeléskor.&lt;/li&gt;
&lt;li&gt;hu.jpattern.model.model.impl.xxxModelImpl az alap entitásunkat reprezentálja. Ezt az osztályt ne módosítsuk, és ne szerezzünk rá referenciát közvetlenül!&lt;/li&gt;
&lt;li&gt;hu.jpattern.model.model.impl.xxxImpl tartalmazza az entitás egyéb, általunk megírt metódusait. Ezt az osztályt szintén beolvasztja a következő build futtatásakor a Liferay.&lt;/li&gt;
&lt;li&gt;Egy állományt emelnék még ki a többi közül, mégpedig a docroot/WEB-INF/lib könyvtárban létrejött ?.service.jar-t, mely az interfészeket és a szerviz "végleges" osztályait tartalmazza. Ezekkel a fejlesztőnek nem kell foglalkoznia.&lt;/li&gt;
&lt;/ul&gt;Használata pofon egyszerű, az xxxLocalServiceUtil osztály statikus metódusain keresztül érhetjük el a Service réteget.&lt;br /&gt;
&lt;pre class="brush: java;"&gt;BookLocalServiceUtil.getBooksCount();&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-3200156270691323994?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/3200156270691323994/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/11/liferay-servide-builder.html#comment-form' title='0 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/3200156270691323994'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/3200156270691323994'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/11/liferay-servide-builder.html' title='Liferay Service Builder'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-3909028557783401857</id><published>2010-09-01T02:10:00.002+02:00</published><updated>2010-09-08T17:17:14.509+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='CodeModel'/><title type='text'>Forrásgenerálás CodeModel segítségével</title><content type='html'>&lt;!--616603a55be644b8a9fe88620e0d6f8b--&gt;&lt;br /&gt;
Minden programozó életében egyszer elérkezik a pont, amikor valamilyen előre egyeztetett séma alapján forrás-álloményokat, azaz működő Java osztályokat kell generálnia. Jelenleg is számtalan eszközzel tudunk forrást generálni, gondoljunk csak az adatbázisból létrejövő entitásokra, vagy egy WSDL alapján generált osztály-struktúrára, sőt a legtöbb IDE alapértelmezetten segítséget nyújt ezen a területen, képes konstruktort, getter-setter metódusokat, stb készíteni pár kattintással. Ha saját magunknak szeretnénk készíteni egy forrás-generátort, természetesen arra is megvan a lehetőség. A &lt;a href="https://jaxb.dev.java.net/"&gt;JAXB&lt;/a&gt;-nek van egy al-projektje, a &lt;a href="https://codemodel.dev.java.net/"&gt;CodeModel&lt;/a&gt;, aminek segítségével megoldható a probléma, ha van elég kitartásunk megérteni a mikéntjét, ugyanis dokumentálva mondhatni egyáltalán nincs az eszköz. Rövid írásom célja, hogy ízelítőt adjon a CodeModel lehetőségeiből, ezért kézenfekvőnek tűnik, hogy ismerkedés gyanánt készítsünk egy olyan generátort, ami átadott paraméter-lista alapján összeállít egy &lt;a href="http://en.wikipedia.org/wiki/Data_transfer_object"&gt;DTO&lt;/a&gt; osztályt. Először is szükségünk lesz a jaxb-xjc csomagra, ugyanis ez tartalmazza a szükséges osztályokat. Magam részéről a DTO típusú osztály-generátort egy saját osztályba csomagoltam.&lt;br /&gt;
&lt;pre class="displaycode; brush: java;"&gt;import com.sun.codemodel.JAnnotationUse;
import com.sun.codemodel.JClassAlreadyExistsException;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JDocComment;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import java.io.Serializable;
import java.util.Map;

/**
 * @author mhmxs
 */
public final class DTO {

    private final String name;
    private final Map&amp;lt;String, Class&amp;gt; parameters;

    public DTO(String name, Map&amp;lt;String, Class&amp;gt; parameters) {
        this.name = name;
        this.parameters = parameters;
    }

    public JCodeModel generateSource() throws JClassAlreadyExistsException {
        JCodeModel codeModel = new JCodeModel();

        //define class header
        JDefinedClass clazz = codeModel._class(name);
        clazz._implements(Serializable.class);
        JAnnotationUse annotation = clazz.annotate(SuppressWarnings.class);
        annotation.param("value", "serial");

        //Add Java-doc to class header
        JDocComment jDocComment = clazz.javadoc();
        jDocComment.add("Simple DTO class : " + name + "\n");
        jDocComment.add("@author mhmxs");

        //Create constructor
        JMethod constr = clazz.constructor(JMod.PUBLIC);

        //Generate getter and setter methods for all parameters
        JMethod method;
        for(String param : parameters.keySet()) {
            //Add parameter to class declaration
            clazz.field(JMod.PRIVATE, parameters.get(param), param);

            //Add parameter to constructor
            constr.param(parameters.get(param), param);
            constr.body().directStatement("this." + param + " = " + param + ";");

            String methodName = param.replaceFirst(String.valueOf(param.charAt(0)),
                String.valueOf(param.charAt(0)).toUpperCase());

            //Create getter method
            method = clazz.method(JMod.PUBLIC, parameters.get(param), "get" + methodName);
            method.body()._return(JExpr._this().ref(param));

            //Create setter method
            method = clazz.method(JMod.PUBLIC, Void.TYPE, "set" + methodName);
            method.param(parameters.get(param), param);
            method.body().directStatement("this." + param + " = " + param + ";");
        }

        return codeModel;
    }
}&lt;/pre&gt;Az osztály-t az alábbi módon tudjuk meghívni.&lt;br /&gt;
&lt;pre class="displacode; brush: java;"&gt;Map&amp;lt;String, Class&amp;gt; parameters = new HashMap&amp;lt;String, Class&amp;gt;();
parameters.put("parameter", String.class);
parameters.put("parameter2", Integer.class);
        
DTO dto = new DTO("a.b.Clazz", parameters);

OutputStream out = new FileOutputStream(new File("/a/b/Clazz.java"));
dto.generateSource().build( new SingleStreamCodeWriter(out));
out.close();
&lt;/pre&gt;Végül az eredmény.&lt;br /&gt;
&lt;pre class="displacode; brush: java;"&gt;package a.b;

import java.io.Serializable;


/**
 * Simple DTO class : a.b.Clazz
 * @author mhmxs
 * 
 */
@SuppressWarnings("serial")
public class Clazz
    implements Serializable
{

    private String parameter;
    private Integer parameter2;

    public Clazz(String parameter, Integer parameter2) {
        this.parameter = parameter;
        this.parameter2 = parameter2;
    }

    public String getParameter() {
        return this.parameter;
    }

    public void setParameter(String parameter) {
        this.parameter = parameter;
    }

    public Integer getParameter2() {
        return this.parameter2;
    }

    public void setParameter2(Integer parameter2) {
        this.parameter2 = parameter2;
    }

}
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-3909028557783401857?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/3909028557783401857/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/09/forrasgeneralas-codemodel-segitsegevel.html#comment-form' title='2 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/3909028557783401857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/3909028557783401857'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/09/forrasgeneralas-codemodel-segitsegevel.html' title='Forrásgenerálás CodeModel segítségével'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-1590108533498780851</id><published>2010-08-19T22:02:00.000+02:00</published><updated>2010-08-19T22:02:38.493+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>META-INF könyvtár kicsit közelebbről</title><content type='html'>A META-INF könyvtáron belül található MANIFEST.MF (továbbiakban MF) állománnyal mindenki találkozik, aki Java binárist készít, vagy felhasznál egy mások által fordított Java forrást. Amikor létrehozunk egy JAR-t vagy valamely módozatát (WAR, EAR), akkor automatikusan belekerül egy példány az állományba, melynek tartalma alapértelmezetten csak a Manifest verziószámát tartalmazza "Manifest-Version: 1.0". A MF állomány JDK 1.2 verziója óta lényegesen egyszerűsödött, korára való tekintettel az ez előtti verzió nem kerül tárgyalásra. A MF állomány kulcs-érték pár alapú metaadat-tárolást valósít meg, és egy JAR-ban (WAR, EAR) csak egy példány lehet belőle, így értelem szerűen az egész JAR-ra, és a benne lévő csomagokra vonatkozó információkat is tárol. Rövid írásom célja, hogy bemutassa a lehetséges alap-beállítások egy részét. Mivel a MF funkcionalitása eléggé széleskörű, kezdve a verzió-követéstől, az elektronikus aláíráson át, a szerzői információkig, egy írásban talán össze sem lehetne foglalni az összes területet, ráadásként az egyes keretrendszerek (Spring, OSGi, stb.) is előszeretettel használják saját csomag-információk tárolására.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;A JAR-ra vonatkozó paraméterek&lt;/h3&gt;&lt;br /&gt;
&lt;h4&gt;Main-Class&lt;/h4&gt;Amennyiben önállóan futtatható JAR-t készítünk, a JVM-nek meg kell adnunk paraméterként, hogy mely osztály "main" metódusa a belépési pont. Mivel ezt körülményes minden indításnál megadni, ezért lehetőségünk van a MF-ben&amp;nbsp; tárolni ezt a beállítást.&lt;br /&gt;
&lt;pre&gt;Main-Class: my.package.startApplication&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;Class-Path&lt;/h4&gt;Az osztály-betöltő ha még nem töltött be egy hivatkozott osztályt, akkor az alapértelmezett útvonalakon elkezdi keresni azt. Ha szerencsések vagyunk, akkor sikerrel jár és az osztály bekerül a memóriába. Amennyiben az alapértelmezettől eltérő útvonalon található az osztály, akkor annak pontos helyet specifikálhatjuk a MF-ben. Lehetőség van több JAR-t is felsorolni szóközzel elválasztva. A fejlesztés könnyítése érdekében helyettesítő karaktereket is használhatunk az útvonal megadásakor.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Class-Path: ./lib/first.jar /usr/lib/secound.jar /usr/lib/java/*&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;Egyéb opcionális paraméterek&lt;/h4&gt;A teljeség igénye nélkül.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Archiver-Version: Plexus Archiver
Created-By: Apache Ant
Built-By: Joe
Build-Jdk: 1.6.0_04
Signature-Version: 1.0&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;Bejegyzésekre vonatkozó paraméterek&lt;/h3&gt;&lt;br /&gt;
&lt;h4&gt;Name&lt;/h4&gt;Lehetőségünk van nevet adnia a JAR-on belül az egyes csomagoknak, osztályoknak vagy erőforrásoknak. A névadás konvenciója, hogy a név értéke a csomag esetén relatív útvonala a fájlstruktúrában, és egy lezáró "/"-karakter, osztály vagy egyéb erőforrás-fájl esetén a teljes relatív elérés. Minden további attribútum ami üres-sor nélkül a név után következik, érvényes lesz a Name-ben hivatkozott elemre.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Name: my/package/&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;Sealed&lt;/h4&gt;A névvel ellátott csomagokat opcionálisan le lehet zárni, ami azt jelenti, hogy a lezárt csomagban lévő összes definiált osztály egyazon JAR-ban található. A lezárásnak biztonsági és verziózási okai lehetnek.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Name: my/package/
Sealed: true&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;Package Versioning Specification&lt;/h4&gt;Verzió-kezelési specifikációban a csomagnak többféle attribútum is megadható opcionálisan, az egyetlen megkötés, hogy ezen beállítások mindegyikének szintén a Name attributumot kell követniük üres-sor hagyása nélkül.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Name: my/package/
Specification-Title: "MF Sample"
Specification-Version: "0.1"
Specification-Vendor: "jpattern"
Implementation-Title: "my.package"
Implementation-Version: "build1"
Implementation-Vendor: "jpattern"&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;Content-Type&lt;/h4&gt;A Content-Type segítségével az adott elem &lt;a href="http://en.wikipedia.org/wiki/MIME"&gt;MIME&lt;/a&gt; tipusát határozhatjuk meg.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Name: my/package/startApplication.properties
Content-Type: text/plain&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;Java-Bean&lt;/h4&gt;A Java-Bean attribútum segítségével adható meg, hogy az adott elem &lt;a href="http://en.wikipedia.org/wiki/JavaBean"&gt;Java Bean&lt;/a&gt; vagy sem.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Name: my/package/startApplication.class
Java-Bean: true&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;*-Digest&lt;/h4&gt;Az aláírással ellátott állományoknál kötelező elemként meg kell adni az állomány &lt;a href="http://wiki.hup.hu/index.php/Hashfüggvény"&gt;hash&lt;/a&gt;ének &lt;a href="http://hu.wikipedia.org/wiki/Base64"&gt;base64&lt;/a&gt; dekódolt reprezentációját. Az aláírás készítésről később.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Name: my/package/startApplication.class
SHA1-Digest: TD1GZt8G11dXY2p4olSZPc5Rj64=&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;MANIFEST.MF állomány készítése Ant taskból&lt;/h3&gt;Természetesen a MF-et nem kell minden alkalommal manuálisan megszerkeszteni, hanem lehetőségünk van Ant taskbol előállítani tartalmát (Mavenből szintén).&lt;br /&gt;
&lt;pre class="displaycode; brush: xml;"&gt;&lt;target depends="build" name="jar"&gt;
    &lt;jar basedir="classes" destfile="application.jar"&gt;
        &lt;manifest&gt;
            &lt;attribute name="Main-Class" value="my.package.startApplication"&gt;
            &lt;/attribute&gt;
        &lt;/manifest&gt;
    &lt;/jar&gt;
&lt;/target&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;Aláírás és hitelesítés&lt;/h3&gt;&lt;br /&gt;
A digitális aláírás fogalmát, és lényegét azt hiszem senkinek sem kell bemutatnom. Mivel a Java eléggé fejlett biztonsággal rendelkezik, elengedhetetlen, hogy az egyes csomagokat ne lehessen hitelesíteni. A Java a művelethez szükséges állományokat szintén a META-INF könyvtárban tárolja, tipikusan *.SF, *.DSA, *.RSA és SIG-* fájlokban. A hitelesítés menete tömören:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Az aláíró egy titkos kulcs segítségével aláírja a JAR-t (pontosabban minden benne lévő állományt).&lt;/li&gt;
&lt;li&gt;A felhasználó a publikus kulccsal ellenőrzi az aláírást.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;h4&gt;Aláírás készítése&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;Aláírás készítésének első lépése, hogy generálnunk kell egy privát kulcsot.&lt;br /&gt;
&lt;pre class="brush: bash"&gt;keytool -genkey -alias jarSigner -keystore storedkeys -keypass passwd -dname "cn=jpatter" -storepass stpasswd&lt;/pre&gt;Mint is csinál ez a parancs? Generál egy kulcsot jarSigner névvel, és a keypass jelszóval védetté teszi. A keystore paraméterben megadott adatbázis-állományban eltárolja a kulcsot, mely állomány ha nem létezik a keytool létrehozza azt, és a storepass-ban megadott jelszóval védi. A dname paraméter specifikál egy un. "X.500 Distinguished Name" bejegyzést, a cn paraméterben megadott egyszerűsített névvel. Az "X.500 Distinguished Name azonosítja a bejegyzéseket a X.509 hitelesítéshez, értéke tetszőleges lehet.&lt;/li&gt;
&lt;li&gt;Az elkészült JAR-unk aláírásához a jarsigner eszközt tudjuk segítségül hívni.&lt;br /&gt;
&lt;pre class="brush: bash"&gt;jarsigner -keystore storedkeys -storepass stpasswd -keypass passwd -signedjar myapp_signed.jar myapp.jar jarSigner&lt;/pre&gt;A parancs lényegében kiszedi az előzőekben létrehozott kulcsot, és a bemeneti JAR állomány minden elemén elvégzi az aláírást, a végeredményt pedig a kimeneti JAR-ba teszi. A JAR-ba pillanva máris megjelent a JARSIGNE.SF és JARSIGNE.DSA, melyek közül az előbbi az egyes állományok hitelesítéséért felel, az utóbbi a publikus kulcs. A JARSIGN.SF állományt megnyitva valami hasonló tárul a szemünk elé:&lt;br /&gt;
&lt;pre&gt;Signature-Version: 1.0
SHA1-Digest-Manifest-Main-Attributes: FPLdz3FFeWLdgX0fUdHTqjUNkpE=
Created-By: 1.6.0_20 (Sun Microsystems Inc.)
SHA1-Digest-Manifest: AJwzuuyn2mg59fYB2qEhUL0PPgI=

Name: my/resources/logo.png
SHA1-Digest: oVtyix/BpiM9iq1fG/nMpy4Xy4Q=

Name: my/package/startApplication.class
SHA1-Digest: TD1GZt8G11dXY2p4olSZPc5Rj64=&lt;/pre&gt;A MF állományunk tartalma is automatikusan megváltozott, az SHA1-Digest bejegyzések kerültek bele.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;h4&gt;Aláírás hitelesítése&lt;/h4&gt;&lt;br /&gt;
A hitelesítés szintén a jarsigner eszközzel történik.&lt;br /&gt;
&lt;pre class="brush: bash"&gt;jarsigner -verify myapp_signed.jar&lt;/pre&gt;eredményként pedig "jar verified" vagy "jar is unsigned" értékeket kaphatjuk.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-1590108533498780851?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/1590108533498780851/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/08/meta-inf-konyvtar-kicsit-kozelebbrol.html#comment-form' title='2 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/1590108533498780851'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/1590108533498780851'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/08/meta-inf-konyvtar-kicsit-kozelebbrol.html' title='META-INF könyvtár kicsit közelebbről'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-8557287385260831694</id><published>2010-07-30T11:57:00.000+02:00</published><updated>2010-07-30T11:57:41.268+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSGi'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='CoolRMI'/><title type='text'>OSGi távoli kapcsolódás CoolRMI segítségével</title><content type='html'>Az &lt;a href="http://jpattern.blogspot.com/2010/07/osgi-szarnyprobalgatas.html"&gt;előző bejegyzés&lt;/a&gt;ben láthattuk miként készíthetünk OSGi keret-rendszer alá szolgáltatást, és hogyan kapcsolódhatunk ehhez a szolgáltatáshoz a keret-rendszeren belül. A mostani írás célja, hogy segédletet nyújtson távoli kapcsolat kialakítására, hiszen elengedhetetlen, hogy az OSGi konténeren kívül eső kód is használni tudja a szolgáltatásokat. Választásom a &lt;a href="http://rizsi.com/public/opensource/coolrmi/coolrmi.html"&gt;CoolRMI&lt;/a&gt; nevű eszközre esett, egyrészt mert magyar fejlesztés, és nyílt-forráskóddal lett publikálva, másrészt mert a környezetem ezt használja, és kipróbált alkalmazás-komponensnek minősült. A CoolRMI két részből tevődik össze. Az egyik fele az OSGi konténerben fut mint szolgáltatás, és várja a beérkező kéréseket, a másik fele pedig az alkalmazásunkban a kapcsolódásért felel.&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Szerezzük be a CoolRMI &lt;a href="http://sourceforge.net/projects/coolrmi/"&gt;legfrissebb verzió&lt;/a&gt;ját.&lt;/li&gt;
&lt;li&gt;Indítsuk el az OSGi konténert.&lt;/li&gt;
&lt;li&gt;Installáljuk majd indítsuk el a CoolRMI-t az OSGi konténerben.&lt;br /&gt;
Valami ilyesmit kell látnunk:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;osgi&amp;gt; ss

Framework is launched.

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.6.0.v20100517
2       ACTIVE      com.rizsi.coolrmi_1.1.0
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Hozzunk létre egy "Plug-in Project"-et az Eclipsben CoolRMI néven, majd a letöltött jar-ból a MANIFEST.MF-et és a forrás-fájlokat másoljuk a projektbe, végül pedig frissítsük a projektet. (nekem a MANIFEST.MF állományba az alábbi sort fel kellett venni: Import-Package: org.osgi.framework;version="1.3.0")&lt;/li&gt;
&lt;li&gt;Nyissuk meg az előzőekben service néven futó projektünket, és a MANIFEST.MF állomány Import-Package szekcióját bővítsük a CoolRMI exportjaival.&lt;/li&gt;
&lt;li&gt;Szerver oldalon a a szolgáltatás regisztrációjában van némi különbség. Jelen esetben nem az OSGi konténerbe kell regisztrálnunk a szolgáltatást, hanem a CoolRMI kontextusába, ezért az Activator.start metódusunkat az alábbira kell alakítani:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: java;"&gt;InetSocketAddress addr = new InetSocketAddress(9001);
  
CoolRMIServer server = new CoolRMIServer(Activator.class.getClassLoader(), addr, true);
  
//Register a service on the id: "service"
//This id an be used client side to find the service on this server.
server.getServiceRegistry().addService(new CoolRMIService("service", SimpleService.class, new SimpleServiceImpl()));
  
server.start();
System.out.println("CoolRMI Service started.");
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Fordítsuk és telepítsük a szolgáltatást.&lt;/li&gt;
&lt;li&gt;Hozzunk létre egy "Java Project"-et, és importáljuk be az imént letöltött jar-t.&lt;/li&gt;
&lt;li&gt;Tegyük elérhetővé a service.SimpleService interfészt, hogy a kliens programunk tudja milyen metódusokat tud meghívni.&lt;/li&gt;
&lt;li&gt;A kliens osztály kódja az alábbi:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: java;"&gt;import java.net.InetSocketAddress;
import service.SimpleService;
import com.rizsi.coolrmi.CoolRMIClient;
import com.rizsi.coolrmi.ICoolRMIProxy;

public class Caller {
 public static void main(String[] args) throws Exception {
  // Create a client that connects to the server on the specified address.
  CoolRMIClient client = new CoolRMIClient( Caller.class.getClassLoader(), new InetSocketAddress("localhost", 9001), true);
  
  try {
   // Get a proxy for the remote service object
   // To access the remote object the remote interface
   // and the service Id must be known
   ICoolRMIProxy remoteService = client.getService(SimpleService.class, "service");
   
   try {
    SimpleService service = (SimpleService) remoteService;
    service.echo("CoolRMI calling.");
   } finally {
    remoteService.disposeProxy();
   }
  } finally {
   client.close();
  }
 }
}&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Futtassuk a klienset, és ellenőrizzük a kimenetet az OSGi konzolon.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;osgi&amp;gt; CoolRMI calling.
&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-8557287385260831694?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/8557287385260831694/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/07/osgi-tavoli-kapcsolodas-coolrmi.html#comment-form' title='0 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/8557287385260831694'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/8557287385260831694'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/07/osgi-tavoli-kapcsolodas-coolrmi.html' title='OSGi távoli kapcsolódás CoolRMI segítségével'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-3471366639105346644</id><published>2010-07-10T11:18:00.001+02:00</published><updated>2010-07-10T11:20:11.663+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSGi'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>OSGi szárnypróbálgatás</title><content type='html'>Bár biztosan sokan ismeritek, de legalább hallottatok az &lt;a href="http://www.osgi.org/Main/HomePage"&gt;Open Services Gateway Initiative&lt;/a&gt;-ról, ismertebb nevén az &lt;a href="http://www.osgi.org/Main/HomePage"&gt;OSGi&lt;/a&gt;-ről, gondoltam én is kísérletet teszek rendszer a bemutatására. A témában kutakodva azonban találtam egy rövid &lt;a href="http://pallergabor.uw.hu/hu/java-app/OSGi.html"&gt;írást&lt;/a&gt; Paller Gábor kollégától, melyben olyan érthetően megfogalmazza a technológia lényegét, hogy bizton állíthatom nekem sem sikerülne jobban, ezért meg sem próbálom. Az elméleti alapok elsajátítása után a gyakorlati megvalósításra szeretném helyezni a hangsúlyt.&lt;br /&gt;
OSGi pluginek fejlesztésére &lt;a href="http://eclipse.org/"&gt;Eclipse IDE&lt;/a&gt;-t választottam, mely amellett, hogy ismeri a MANIFES.MF állomány függőségeit kezelni, rendelkezik beépített OSGi konténerrel (&lt;a href="http://www.eclipse.org/equinox/"&gt;Equinox&lt;/a&gt;) is. Szerencsére a hazai bloggerek már több ízben is feszegették a témát, ennek köszönhetően pl. a&amp;nbsp;&lt;a href="http://jtechnics.anzix.net/2009/05/10/osgi-nevsorolvasas/"&gt;jTechnics&lt;/a&gt; hasábjain olvashatunk a különféle konténer-implementációkról.&lt;br /&gt;
Megvalósítandó feladatként készítsünk egy bundlet, amely kiajánl egy szolgáltatást, amit egy másik bundeből meghívunk.&lt;br /&gt;
&lt;h3&gt;A szerviz:&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Első lépésként hozzunk létre az Eclipsben egy "Plug-in Project"-et service néven, és az "on OSGi framework" opciónál állítsuk be az Equinox-ot.&lt;/li&gt;
&lt;li&gt;Hozzuk létre a service.SimpleService interfészt:&lt;br /&gt;
&lt;pre class="brush: java;"&gt;package service;

public interface SimpleService {
 public void echo(Object message);
}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Majd hozzuk létre service.SimpleServiceImpl néven az szolgáltatás implementációját:&lt;br /&gt;
&lt;pre class="brush: java;"&gt;package service;

public class SimpleServiceImpl implements SimpleService {

 @Override
 public void echo(Object message) {
  System.out.println(message);
 }
}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Szerkesszük a service.Activator osztályt a következőre:&lt;br /&gt;
&lt;pre class="brush: java;"&gt;package service;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;

public class Activator implements BundleActivator {

 @Override
 public void start(BundleContext context) throws Exception {
  context.registerService(SimpleService.class.getName(), new SimpleServiceImpl(), new java.util.Hashtable());
  
  //Check service
  ServiceTracker serviceTracker = new ServiceTracker(context, SimpleService.class.getName(), null);
  serviceTracker.open();
  
  SimpleService simpleLogService = (SimpleService) serviceTracker.getService();
  if(simpleLogService != null)
   System.out.println("Service started.");
  else
   System.out.println("Service init failed!");
  
  serviceTracker.close();
 }

 @Override
 public void stop(BundleContext context) throws Exception {  
  System.out.println("Service stopped.");
 }
}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;A META-INF/MANIFEST.MF állomány szerkesztésével készíthetjük fel bundlet a konténerrel való együttműködésre. Eclipsben az Import-Package szerkesztésével tudjuk a "Plug-in Dependencies"-eket szerkeszteni, az IDE automatikusan importálja az itt megadott csomagokat. Fontos továbbá, hogy az Export-Packagenél kiajánljuk a service csomagot, mert ellenkező esetben a kliens oldalon "class SimpleServiceImpl cannot be cast to class SimpleService" exceptiont fogunk kapni.&lt;br /&gt;
&lt;pre&gt;Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Service
Bundle-SymbolicName: service
Bundle-Version: 1.0.0
Bundle-Activator: service.Activator
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.osgi.framework;version="1.3.0",org.osgi.util.tracker;version="1.3.1"
Export-Package: service;version="1.0.0"
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;A MANIFEST.MF állomány-&amp;gt;jobbklikk-&amp;gt;Run As-&amp;gt;Run Configuration ablakban tudjuk beállítani a konténert a teszteléshez.&lt;/li&gt;
&lt;li&gt;Fordításhoz vagy írunk saját ant/maven scriptet, vagy Exportáljuk a bundlet JAR formájában. Szeretném megjegyezni, hogy az Eclipse alapértelmezetten generálja a MANIFEST.MF állományt a jar-ba, viszont ebben az esetben a konténer nem fog tudni mit kezdeni a bundleval, ezért a "Use existing manifest from workspace" opció alatt (utolsó lépés) adjuk meg a megfelelő állományt.&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;A kliens:&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Ismét egy hasonlóan paraméterezett "Plug-in Projct"-re lesz szükségünk, de ezúttal caller néven.&lt;/li&gt;
&lt;li&gt;A caller.Activate osztályt hozzuk az alábbi formára:&lt;br /&gt;
&lt;pre class="brush: java;"&gt;package caller;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

import service.SimpleService;

public class Activator implements BundleActivator {

 @Override
 public void start(BundleContext context) throws Exception {
  //Another way to connect a service.
  ServiceReference reference = context.getServiceReference(SimpleService.class.getName());
  SimpleService service = (SimpleService) context.getService(reference);
  
  if (service != null)
   service.echo("Service called.");
  else
   System.out.println("Service call error!");
 }

 @Override
 public void stop(BundleContext context) throws Exception {
 }
}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;A service.SimpleService interfészt mindenképpen tegyük elérhetővé ebben a projektben, az egyszerűség kedvéért hozzuk létre az objektumot.&lt;/li&gt;
&lt;li&gt;A META-INF/MANIFEST.MF állomány tartalma legyen a következő:&lt;br /&gt;
&lt;pre&gt;Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Caller
Bundle-SymbolicName: caller
Bundle-Version: 1.0.0
Bundle-Activator: caller.Activator
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.osgi.framework;version="1.3.0",service;version="1.0.0"
&lt;/pre&gt;Fontos, hogy a szervizben exportált service csomagot itt importáljuk. &lt;/li&gt;
&lt;li&gt;A szerviznél leírtakkal azonos módon tesztelhetjük és fordíthatjuk a bundlet (nekem voltak problémák, amikor az IDE-ből próbáltam tesztelni a két bundlet együtt).&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;A konténer:&lt;/h3&gt;Utolsó egységként a konténert kell megismernünk, melynek vezérlése eltérő a különböző implementációk esetén. Az Equinoxot választva az alábbiakat kell tennünk.&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Szerezzük be a nekünk megfelelő verziót, jelenleg a 3.6-os a legfrissebb.&lt;/li&gt;
&lt;li&gt;A `java -jar org.eclipse.osgi_3.6.0(.*?).jar -console` parancs futtatásával indíthatjuk el a konténert.&lt;/li&gt;
&lt;li&gt;Installáljuk a bundlekat:&lt;br /&gt;
&lt;pre&gt;osgi&amp;gt; install file:///path/to/file/service.jar
Bundle id is 6

osgi&amp;gt; install file:///path/to/file/caller.jar
Bundle id is 7
&lt;/pre&gt;Eltávolítani az uninstall paranccsal, frissíteni pedig az updatetel lehet.&lt;/li&gt;
&lt;li&gt;Az ss paranccsal lekérdezhetjük a telepített bundlek állapotát, mindkettőnek INSTALLED állapotban kell lennie:&lt;br /&gt;
&lt;pre&gt;osgi&amp;gt; ss

Framework is launched.

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.6.0.v20100517
6       INSTALLED   service_1.0.0.qualifier
7       INSTALLED   caller_1.0.0.qualifier
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Utolsó lépésként indítsuk el mindkét bundlet:&lt;br /&gt;
&lt;pre&gt;osgi&amp;gt; start service
Service started.

osgi&amp;gt; start caller
Service called.
&lt;/pre&gt;A bundle neve helyett használhatjuk az azonosítóját.&lt;/li&gt;
&lt;li&gt;A stop paranccsal állítjuk le a bundlekat:&lt;br /&gt;
&lt;pre&gt;osgi&amp;gt; stop service
Service stopped.
&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-3471366639105346644?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/3471366639105346644/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/07/osgi-szarnyprobalgatas.html#comment-form' title='0 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/3471366639105346644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/3471366639105346644'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/07/osgi-szarnyprobalgatas.html' title='OSGi szárnypróbálgatás'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-4687097572957789763</id><published>2010-06-24T16:41:00.071+02:00</published><updated>2010-11-22T13:14:56.336+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='J2EE'/><category scheme='http://www.blogger.com/atom/ns#' term='Portlet'/><category scheme='http://www.blogger.com/atom/ns#' term='Liferay'/><title type='text'>Barátkozás a Liferay Portlettel, telepítés</title><content type='html'>Az utóbbi időben sajnos, vagy inkább szerencsére több időt szántam a tanulásra, mint az írásra. Egy munka-hely váltás kapcsán&amp;nbsp; találkoztam a Liferay Portletek téma-körével, és mivel hosszas küzdelem árán sikerült beüzemelni a dolgot, gondoltam talán másoknak is hasznos lehet, ha lejegyzem.&lt;br /&gt;
A &lt;a href="http://www.liferay.com/"&gt;Liferay&lt;/a&gt; egy Java alapú, nyílt forrás-kódú, "Enterprise" CMS rendszer, ami annyiba tér el más hasonló alkalmzásoktól, hogy a tartalmat &lt;a href="http://developers.sun.com/portalserver/reference/techart/jsr168/"&gt;Portlet&lt;/a&gt;ek segítségével állítja elő. A Portletek lényegét röviden úgy foglalhatnánk össze, hogy az egyes hagyományos értelembe vett modulok külön életet élnek, és egy HTML oldal legenerálása a szerveren nem a megszokott módon történik, hanem a Portlet konténer, a megjelenítendő HTML-t "össze-ollózza" a Portletektől, azaz a Portleteket megkéri, hogy az állapotuknak megfelelő HTML kimenetet biztosítsák számára. Portlet konténerből számtalan implementáció létezik, ki jobban, ki kevésbé tér el a specifikációtól, a Liferay fejlesztői úgy döntöttek, hogy saját megoldást dolgoznak ki, így született meg a Liferay Portlet. Számomra a megismerést nagyban nehezítette a név-választás, ami egyébként logikus, ugyanis a netet böngészve, linkre kattintva nehéz hirtelen eldönteni, hogy vajon a portálról, vagy a portletről van éppen szó.&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;A tanulás/fejlesztés első lépése, hogy &lt;a href="http://www.liferay.com/downloads/liferay-portal/overview"&gt;letöltjük&lt;/a&gt; a conténert. Bár széles palettán válogathatunk a társított szerverek terén - Tomcat, JBoss, Glassfish...-, mégiscsak a Liferay fejlesztők száj-íze, és verziói szerint kell dolgoznunk. Személy szerint nekem nem szimpatikus, hogy csak egyben tudjuk beszerezni a web-konténert, a Portlet-konténert, és ráadásként a Liferay Portalt, találkoztam olyan Portlet-konténerrel, amely meglévő alkalmazás-szerverünket frissítette, meghagyva a választás szabadságát, illetve futó alkalmazásainkat.&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Kicsomagolás közben bőven lesz időnk tanulmányozni a &lt;a href="http://docs.liferay.com/portal/4.2/official/liferay-portlet-development-guide-4.2/onepage/"&gt;dokumentációt&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Miután elindítottuk a szervert azonmód elérhető a Lifery Portal rendszer (jó esetben a &lt;a href="http://localhost:8080/"&gt;http://localhost:8080&lt;/a&gt; címen), amire Portlet fejlesztőként egyelőre nem lesz szükségünk.&lt;/li&gt;
&lt;li&gt;A dokumentáció szerint függőségként telepítenünk kell az &lt;a href="http://ant.apache.org/"&gt;Apache Ant&lt;/a&gt;ot (Mavennel is működésre lehet bírni, de ezt nem próbáltam), valamint a &lt;a href="http://jikes.sourceforge.net/"&gt;Jikes&lt;/a&gt; fordítót.&lt;/li&gt;
&lt;li&gt;Az első Portletünk létrehozását érdemes a &lt;a href="http://www.liferay.com/downloads/liferay-portal/additional-files"&gt;Liferay Plugins SDK&lt;/a&gt;n keresztül elvégezni, amely számtalan mintával könnyíti meg a fejlsztést.&lt;/li&gt;
&lt;li&gt;Az SDK gyökér-könyvtárában található build.properties állományban állítsuk be a kívánt web-konténert, a megfelelő elérésekkel. Amennyiben több felhasználó is használja ugyanazt az SDK-t, a buil.properties mellé hozzunk létre felhasználónként egy-egy build.${username}.properties fájlt, amelyben perszonalizálni tudjuk a beállításokat.&lt;/li&gt;
&lt;li&gt;Következő lépésként az SDK portlets könyvtárában hozzunk létre egy hello-world projektet.&lt;br /&gt;
&lt;pre class="brush: shell;"&gt;./create.sh hello-world "Hello World"
Buildfile: build.xml

create:
    [unzip] Expanding: /media/data/code/liferay/sdk/portlets/portlet.zip into /media/data/code/liferay/sdk/portlets/hello-world-portlet
    [mkdir] Created dir: /media/data/code/liferay/sdk/portlets/hello-world-portlet/docroot/WEB-INF/tld
     [copy] Copying 6 files to /media/data/code/liferay/sdk/portlets/hello-world-portlet/docroot/WEB-INF/tld

BUILD SUCCESSFUL
Total time: 1 second
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;A script által létrehozott hello-world-portlet könyvtárba lépve vegyük rá az Antot, hogy készítsen egy buildet.&lt;br /&gt;
&lt;pre class="brush: shell;"&gt;ant deploy
Buildfile: build.xml

compile:

merge:
    [mkdir] Created dir: /media/data/code/liferay/sdk/portlets/hello-world-portlet/docroot/WEB-INF/classes
    [mkdir] Created dir: /media/data/code/liferay/sdk/portlets/hello-world-portlet/docroot/WEB-INF/lib
     [copy] Copying 5 files to /media/data/code/liferay/sdk/portlets/hello-world-portlet/docroot/WEB-INF/lib
    [javac] Compiling 1 source file to /media/data/code/liferay/sdk/portlets/hello-world-portlet/docroot/WEB-INF/classes

merge:

war:

clean-portal-dependencies:
      [zip] Building zip: /media/data/code/liferay/sdk/dist/hello-world-portlet-5.2.3.1.war

deploy:
     [copy] Copying 1 file to /media/data/code/liferay/deploy

BUILD SUCCESSFUL
Total time: 4 seconds
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Utolsó simításként annyi van hátra, hogy Bruno (Admin) nevében jelentkezzünk be az alapértelmezett &lt;a href="http://localhost:8080/web/guest"&gt;Portal oldalon&lt;/a&gt; (email: bruno@7cogs.com, password: bruno, de szerencsére kattintós módszer is van), és a Welcome -&gt; Add Application menüpont alatt adjuk hozzáadjuk a "Hello World"-öt az oldalhoz (érdemes rákeresni).&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;s&gt;Szeretném megjegyezni, hogy eléggé sok fejfájást okozott az, hogy eleinte a megszokott módon szerettem volna az alkalmazás-fejlesztést vezérelni az IDE-ből. Ennek érdekében először a &lt;a href="http://www.liferay.com/web/gregory.amerson/blog/-/blogs/liferay-ide-1-0-0-released"&gt;Liferay Eclipse Plugin&lt;/a&gt;jével próbálkoztam (elég sokat), amit végül nem sikerült rávenni, hogy tegye a dolgát. Ezután megpróbáltam olyan projektet létrehozni az IDE-ben, ami kapcsolatban áll a web-konténerrel, és vezérli a szinkronizációt és magát a szervert (itt is próbálkoztam pár kombinációval hátha csak én vagyok a hüje), de sajnos ez sem vezetett eredményre. Az járható út egyelőre, hogy az IDE-ből fejlesztés közben/után WAR fájlba csomagolva a konténer auto-deploy könyvtárába exportáljuk a produktumot, és a konzolból indított szerver pedig teszi a dolgát. Szerény véleményem, hogy ez eléggé fa-pados, és időigényes módszer, hiszen a konténer az egész alkalmazást újra húzza, és nem csak a szükséges részt frissíti.&lt;/s&gt; &lt;b&gt;Szerk.: A &lt;a href="http://www.liferay.com/web/gregory.amerson/blog/-/blogs/liferay-ide-1-0-0-released"&gt;Liferay Eclipse Plugin&lt;/a&gt; sajnos csak 6-os verziónál &gt;= Liferayyel kompatibilis, így csak az új fejlesztések tehetők kényelmesebbé vele. Régebbi verzió esetén eléggé fapados sajnos a fejlesztés.&lt;/b&gt; &lt;br /&gt;
Pozitívumként tapasztaltam, hogy létezik &lt;a href="http://www.liferay.com/community/forums/-/message_boards/category/4087838"&gt;Magyar Liferay közösség&lt;/a&gt;, továbbá egy hazai Liferay specialista az &lt;a href="http://www.i-logic.hu/liferay"&gt;I-Logic&lt;/a&gt; képében, és az elengedhetetlen &lt;a href="http://www.facebook.com/liferay?ref=ts"&gt;Facebook&lt;/a&gt; csoport.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-4687097572957789763?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='enclosure' type='' href='http://www.liferay.com/' length='0'/><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/4687097572957789763/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/06/baratkozas-liferay-portlettel-telepites.html#comment-form' title='2 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/4687097572957789763'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/4687097572957789763'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/06/baratkozas-liferay-portlettel-telepites.html' title='Barátkozás a Liferay Portlettel, telepítés'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-3844759391915044677</id><published>2010-05-16T20:21:00.006+02:00</published><updated>2010-05-16T20:34:06.618+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Trigger'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Adott pont vonzáskörzetében való keresés MySQL segítségével</title><content type='html'>&lt;div&gt;Azt a feladatot kaptam, hogy saját adatbázisból vendéglátó-egységeket tegyek kereshetővé, és a találatokat jelenítsem meg egy térképen. Hogy ne legyen olyan egyszerű a feladat, további kérés volt, hogy Budapesten a szomszédos kerületek találatait is mutassam meg, egyébként pedig 50 km-es körzetben lévőket. Bár a megoldás nem kapcsolódik közvetlenül a Java-hoz, mégis úgy gondoltam talán érdekes lehet. Igyekeztem úgy kivitelezni a dolgot, hogy a &lt;a href="http://dev.mysql.com/"&gt;MySQL&lt;/a&gt; adatbázisból ne kerüljön ki olyan érték, amelyet nem kell megjeleníteni. &lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Lássuk először az 50 km-es körzet problémáját:&lt;br /&gt;
Először is létrehoztam két triggert, amik a vendéglátó-egység beszúrása vagy módosítása után futnak le, és egész egyszerűen annyit tesznek, hogy a beállított longitude és latitude értékből a coordinate mezőben eltárolnak egy &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/gis-class-point.html"&gt;POINT&lt;/a&gt; objektumot. A POINT beépített MySQL objektum, így értelem-szerűen a mező típusának is az van deklarálva.&lt;br /&gt;
&lt;/div&gt;&lt;pre class="brush: sql;"&gt;DELIMITER //

CREATE TRIGGER RefreshHorecaPointInsert BEFORE INSERT ON `horeca`
FOR EACH ROW
BEGIN
    SET NEW.`coordinate` = POINT(NEW.`longitude`, NEW.`latitude`);
END//

CREATE TRIGGER RefreshHorecaPointUpdate BEFORE UPDATE ON `horeca`
FOR EACH ROW
BEGIN
    SET NEW.`coordinate` = POINT(NEW.`longitude`, NEW.`latitude`);
END//

DELIMITER ;
&lt;/pre&gt;&lt;div&gt;A következő lépés már igen egyszerű, mivel a MySQL (is) rendelkezik beépített geometriai funkciókkal. Feladatunk annyi, hogy a &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/gis-class-polygon.html"&gt;POLYGONE&lt;/a&gt; objektumot felhasználva az első találat koordinátái köré egy tetszőleges sokszöget rajzoljunk. Bár az 50 km-es körzet meghatározásához egy kört kellene rajzolni, de annak bonyolultsága és gyakorlati haszna között fennálló aránytalanság miatt, én a négyszöget választottam.&lt;br /&gt;
&lt;/div&gt;&lt;pre class="brush: java;"&gt;String polygone = "POLYGON((" + (horeca.getLongitude() - 0.32) + " " + (horeca.getLatitude() - 0.22) + ", " + (horeca.getLongitude() + 0.32) + " " + (horeca.getLatitude() - 0.22) + ", " + (horeca.getLongitude() + 0.32) + " " + (horeca.getLatitude() + 0.22) + ", " + (horeca.getLongitude() - 0.32) + " " + (horeca.getLatitude() + 0.22) + ", " + (horeca.getLongitude() - 0.32) + " " + (horeca.getLatitude() - 0.22) + "))";
String query = "SELECT h FROM Horeca h WHERE MBRContains(PolygonFromText('" + polygone + "'), coordinate)";
&lt;/pre&gt;&lt;div&gt;Megjegyzem a geometriai funkciók elméletileg 5.0-ás verzió óta léteznek MySQL-ben, elég sok helyen olvastam, hogy hibásan működik, meg, hogy aki ilyet akar használjon PostgreSQL-t. Én is belefutottam olyan kellemetlenségbe, ami miatt frissíteni kellett a szervert 5.1.x-re (a SELECT POINT(12, 12) lefutott, de a UPDATE horeca SET coordinate = POINT(longitude, latitude) már nem).&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;A Budapest szomszédos kerületeinek kérdés-köre nem egy bonyolult dolog, pusztán azért tárgyalom, hogy teljes legyen a kép. Az adatbázis kímélése érdekében ismét egy triggert írtam, ami budapesti cím beszúrása vagy szerkesztése esetén az irányító számból eltárolja a középső 2 karaktert, mint kerületet. Az egységes kereshetőség érdekében hagytam minden kerületet két karakteresre.&lt;br /&gt;
&lt;/div&gt;&lt;pre class="brush: sql;"&gt;DELIMITER //

CREATE TRIGGER SetDistrict BEFORE INSERT ON `horeca`
FOR EACH ROW
BEGIN
    IF NEW.`postal_code` &lt; 2000 THEN
        SET NEW.`district` = SUBSTR(NEW.`postal_code`, 2, 2);
    END IF;
END;

DELIMITER ;
&lt;/pre&gt;
&lt;div&gt;Ezek után létrehoztam egy táblát, amely a szomszédos kerületek mátrixát tartalmazza.
&lt;/div&gt;&lt;pre class="brush: sql;"&gt;INSERT INTO `district_neighbors` (`reference`, `neighbor`) VALUES
('01', '02'),('01', '12'),('01', '11'),('01', '05'),('02', '12'),('02', '03'),('02', '05'),('02', '13'),('03', '04'),('03', '13'),('04', '13'),('04', '14'),('04', '15'),('05', '06'),('05', '07'),('05', '13'),('06', '07'),('06', '13'),('06', '14'),('07', '08'),('07', '09'),('07', '14'),('08', '09'),('08', '10'),('08', '14'),('09', '10'),('09', '11'),('09', '19'),('09', '20'),('09', '21'),('10', '14'),('10', '16'),('10', '17'),('10', '18'),('10', '19'),('11', '12'),('11', '21'),('11', '22'),('13', '18'),('13', '19'),('13', '20'),('13', '21'),('14', '15'),('14', '16'),('15', '16'),('17', '18'),('18', '19'),('18', '20'),('18', '23'),('19', '20'),('20', '21'),('20', '23'),('21', '22'),('21', '23');
&lt;/pre&gt;&lt;div&gt;Az alábbi lekérdezéssel pedig könnyedén kinyerhető adott kerület és a vele szomszédosak az adatbázisból.
&lt;/div&gt;&lt;pre class="brush: sql;"&gt;String query = "SELECT h FROM Horeca h WHERE district = :district OR district IN (SELECT IF(reference = :district, neighbor, reference) FROM district_neighbors WHERE reference = :district OR neighbor = :district)";
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-3844759391915044677?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/3844759391915044677/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/05/adott-pont-vonzaskorzeteben-valo.html#comment-form' title='0 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/3844759391915044677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/3844759391915044677'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/05/adott-pont-vonzaskorzeteben-valo.html' title='Adott pont vonzáskörzetében való keresés MySQL segítségével'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-718268385143250652</id><published>2010-04-28T15:36:00.001+02:00</published><updated>2010-04-28T15:44:02.945+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Glassfish'/><category scheme='http://www.blogger.com/atom/ns#' term='Apache'/><title type='text'>Weboldal beüzemelése Linux, Apache, Glassfish alapon</title><content type='html'>&lt;div&gt;Java-s web-alkalmazások fejlesztése során gyorsan felmerül az igény arra, hogy az oldal elérhető legyen mindenféle portszám megadása nélkül, hiszen vég-felhasználóink általában nem szakavatott fejlesztők, vagy rendszer tervező mérnökök, akiktől nem idegen az efféle címzés. A probléma megoldására több lehetséges megoldás is létezik, az általam felvázolt lehetőség csak egy a sok közül. Operációs rendszernek Linuxot választottam, kedvenc alkalmazás-szerverem pedig a Glassfish.&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;A beállítás lépései:&lt;br /&gt;
&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Glassfish domain létrehozása.&lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;# asadmin create-domain --adminport 4848 --savemasterpassword domain1
&lt;/pre&gt;&lt;div&gt;A savemasterpassword opcióra azért van szükség, hogy a szerver indításakor és leállításakor ne kelljen a masterjelszavat megadni. Ellenkező esetben csak kézzel tudjuk a szervert indítani és/vagy leállítani.&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Indító script megírása, és a megfelelő runlevel-be helyezése.&lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;#!/bin/sh
ase "$1" in
start)
    ulimit -Hn 10240
    ulimit -Sn 10240
    su glassfish /path_to_glassfish/bin/asadmin start-domain domain1
    ;;
stop)
    su glassfish /path_to_glassfish/bin/asadmin stop-domain domain1
    ;;
restart)
    su glassfish /path_to_glassfish/bin/asadmin stop-domain domain1
    ulimit -Hn 10240
    ulimit -Sn 10240
    su glassfish /path_to_glassfish/bin/asadmin start-domain domain1
    ;;
*)
    echo $"usage: $0 {start|stop|restart}"
    exit 1
esac
&lt;/pre&gt;&lt;div&gt;Az első említésre méltó dolog, hogy én létrehoztam egy glassfish felhasználót a rendszerben, és annak nevében/jogosultságával telepítettem az alkalmazás-szervert. Ennek elsősorban biztonsági okai vannak, hiszen így az alkalmazás-szerver csak a "mezei" felhasználó hatáskörében tud tevékenykedni. A második dolog ami szemet szúrhat az ulimit parancs. A Linux kernelben meg van határozva, hogy mekkora darab-számú állományt nyithat meg egy alkalmazás/felhasználó. Ez a szám alapértelmezetten 1024, amiből a Glassfish indulás után elhasznál 8-900-at, így eléggé kis terhelés esetén is átlépi a határt. A kiadott ulimit parancs az adott processre, és az abból induló alprocessekre vonatkozik, ezért nem érdemes rendszer-szinten növelni a limitet, elég az init scriptben beállítani a kívánt értéket. A pontos érték megállapítása terheléses teszt után hangolható, ám kezdésnek érdemes 10240-re venni.&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Apache web-szerver beállítása&lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;NameVirtualHost *:80

&amp;lt;VirtualHost *:80&amp;gt;
    ServerName www.foo.bar
    ProxyPass / http://localhost:8080/foo.bar-war/
    ProxyPassReverse / http://localhost:8080/foo.bar-war/
    ProxyPassReverseCookieDomain localhost:8080/foo.bar-war www.foo.bar
    ProxyPassReverseCookiePath / /
    ProxyVia Off
    ProxyPreserveHost On
&amp;lt;/VirtualHost&amp;gt;
&lt;/pre&gt;&lt;div&gt;Mivel a felhasználók az URL begépelésével a szerver 80-as portjára csatlakoznak, ésszerű megoldás, ha egy web-szervert telepítünk erre a portra, és a web-szerverből proxyzzuk át a megfelelő kéréseket az alkalmazás-szerver felé. A proxyzáshoz a mod_proxy és mod_proxy_http modulokat kell betölteni. A ProxyPass és ProxyPassReverse opciókkal magát a proxyzás útvonalát állítjuk be, míg a ProxyPassReverseCookieDomain és ProxyPassReverseCookiePath opciókkal a Cookie-k tárolásának módját írjuk elő. Az utóbbi 2 opció elhagyása esetén nem tudjuk a tárolt Cookie-kat elérni, mivel azok a www.foo.bar domainen lesznek bejegyezve, ebből kifolyólag Session azonosítót sem tudunk Cookie-ban tárolni.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-718268385143250652?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/718268385143250652/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/04/weboldal-beuzemelese-linux-apache.html#comment-form' title='3 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/718268385143250652'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/718268385143250652'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/04/weboldal-beuzemelese-linux-apache.html' title='Weboldal beüzemelése Linux, Apache, Glassfish alapon'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-3974779212489348800</id><published>2010-04-27T10:54:00.008+02:00</published><updated>2010-04-27T13:19:10.337+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='JDBC'/><category scheme='http://www.blogger.com/atom/ns#' term='Log4j'/><title type='text'>JDBC loggolás jdbcdslog segítségével</title><content type='html'>&lt;div&gt;Találtam egy remek kis eszközt, amely képes loggolni a JDBC rétegben történt eseményeket. Az SQL-ek mellett eltárolja a lekérdezések eredményeit is, így pontosan képet kaphatunk arról, hogy mi is történik valójában az adatbázis rétegben. Az alkalmzás a jdbcdslog nevet viseli, és a &lt;a href="http://code.google.com/p/jdbcdslog/"&gt;Google Code&lt;/a&gt; szolgáltatáson keresztül érhető el nyílt forráskóddal.&lt;/div&gt;&lt;div&gt;Telepítése pár lépésben:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Az &lt;a href="http://code.google.com/p/jdbcdslog/downloads/list"&gt;aktuális verzió&lt;/a&gt; beszerezhető az oldalról. Érdemes a "distribution" kiadást letölteni, mert abban van pár függőség is csomagolva.&lt;/li&gt;
&lt;li&gt;Az Apache &lt;a href="http://logging.apache.org/log4j/1.2/download.html"&gt;Log4j&lt;/a&gt;-re épül a logger, így azt is érdemes beszerezni.&lt;/li&gt;
&lt;li&gt;Mivel a példa-program a legegyszerűbb módozatot mutatja be, így én a jdbcdslog forrását bemásoltam az alkalmazásomba, illetve a slf4j-api-1.5.10.jar, slf4j-log4j12-1.5.10.jar, log4j-1.2.16.jar jar-okat importáltam. (A jdbcdslog forrásában van hiba, azokat érdemes figyelmen kívül hagyni fordításkor!)&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;A telepítésről &lt;a href="http://code.google.com/p/jdbcdslog/wiki/UserGuide"&gt;bővebben&lt;/a&gt;. &lt;/div&gt;&lt;div&gt;Ezután következhet a kapcsolódás megírása. A dolog nyitja abban rejlik, hogy a JDBC kéréseket a jdbcdslog proxyzza át a JDBC providernek, ehhez a kapcsolatot át kell adni a loggernek. &lt;/div&gt;&lt;pre class="brush: java"&gt;Connection conn = null;
try {
    PropertyConfigurator.configure(Main.class.getResource("log4j.properties").getFile());
    conn = DriverManager.getConnection("jdbc:mysql://localhost/mysql", "root", "password");
    Connection loggingConnection = ConnectionLoggingProxy.wrap(conn);
    Statement statement = loggingConnection.createStatement();
    statement.executeQuery("SELECT * FROM user");
    ResultSet resultSet = statement.getResultSet();
    while (resultSet.next()) {
        System.out.println(resultSet.getString("User"));
    }
    statement.close();
    resultSet.close();
} catch (Exception e) {
} finally {
    if (conn != null) {
        try {
            conn.close();
        } catch (Exception e) {}
    }
}
&lt;/pre&gt;&lt;div&gt;A Log4j beállítása a log4j.properties fájlban történik, ennek rejtelmeibe nem mélyednék bele, az egy külön bejegyzést igényelne. &lt;/div&gt;&lt;pre&gt;#Create logger named A1
log4j.rootLogger=DEBUG, A1
#Write log to test.log over FileAppender
log4j.appender.A1=org.apache.log4j.FileAppender
log4j.appender.A1.File=test.log
log4j.appender.A1.Append=true
#Set the layout to PatternLayout and set pattern
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%4d{dd MMM yyyy HH:mm:ss,SSS} %-5p - %c: %m\n
&lt;/pre&gt;&lt;div&gt;Futtatva kódunkat az alábbi eredményt kapjuk. Én egy frissen telepített MySQL adatbázis mysql táblájából olvastam ki a felhasználókat. &lt;/div&gt;&lt;pre&gt;root
root
root
&lt;/pre&gt;&lt;div&gt;A test.log tartalma: &lt;/div&gt;&lt;pre&gt;27 Apr 2010 14:36:43,992 DEBUG - org.jdbcdslog.LogUtils: createLogEntry()
27 Apr 2010 14:36:43,993 INFO  - org.jdbcdslog.StatementLogger: java.sql.Statement.executeQuery SELECT * FROM user 110 ms.
27 Apr 2010 14:36:43,999 INFO  - org.jdbcdslog.ResultSetLogger: java.sql.ResultSet.next {'localhost', 'root', '*080C34F746094F116AE54467CF39EA994A0FF57F', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', '', [B@4f80d6, [B@4f80d6, [B@4f80d6, 0, 0, 0, 0}
27 Apr 2010 14:36:44,003 INFO  - org.jdbcdslog.ResultSetLogger: java.sql.ResultSet.next {'linux-brsg', 'root', '', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', '', [B@4f80d6, [B@4f80d6, [B@4f80d6, 0, 0, 0, 0}
27 Apr 2010 14:36:44,004 INFO  - org.jdbcdslog.ResultSetLogger: java.sql.ResultSet.next {'127.0.0.1', 'root', '', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', '', [B@4f80d6, [B@4f80d6, [B@4f80d6, 0, 0, 0, 0}
27 Apr 2010 14:36:44,007 INFO  - org.jdbcdslog.ResultSetLogger: java.sql.ResultSet.next {'localhost', '', '', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', '', [B@4f80d6, [B@4f80d6, [B@4f80d6, 0, 0, 0, 0}
27 Apr 2010 14:36:44,008 INFO  - org.jdbcdslog.ResultSetLogger: java.sql.ResultSet.next {'linux-brsg', '', '', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', '', [B@4f80d6, [B@4f80d6, [B@4f80d6, 0, 0, 0, 0}
&lt;/pre&gt;&lt;div&gt;Perzisztes réteg használata esetén természetesen az itt vázol megoldás nem kivitelezhető, de szerencsére a fejlesztők két további módszert is építettek az alkalmazásba. Van lehetőség "JDBC Driver" és "JDBC DataSource" proxyzásra is, ezeket választva csak a kapcsolódási URL-t kell módosítani, az alkalmazásunk érintetlen marad.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-3974779212489348800?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/3974779212489348800/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/04/jdbc-loggolas-jdbcdslog-segitsegevel.html#comment-form' title='1 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/3974779212489348800'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/3974779212489348800'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/04/jdbc-loggolas-jdbcdslog-segitsegevel.html' title='JDBC loggolás jdbcdslog segítségével'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-8992260885029170573</id><published>2010-04-14T22:43:00.002+02:00</published><updated>2010-04-16T13:45:56.409+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>HTTPS kapcsolódás self-signed és/vagy lejárt tanusítvánnyal ellátott szolgáltatáshoz</title><content type='html'>&lt;div&gt;Egy on-line fizetéses alkalmazás fejlesztése közben futottam bele abba a problémába, hogy https protokollon keresztül kell kapcsolódni a banki rendszerhez. Első hallásra nem is tűnik olyan bonyolultnak a dolog, azonban a megvalósítás közben belefutottam egy igen kellemetlen körülménybe. A teszt rendszer tanusítványa lejárt. Az ügyeletes rendszer-gazdával konzultálva megnyugtatott, hogy az éles rendszerben nem lesz probléma,... egy darabig,... még jó, hogy szóltam! Álmomban sem gondoltam volna, hogy ilyen előfordulhat egy banknál, mégha tesztrendszerről is van szó.&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;A kapcsolódáshoz a &lt;a href="http://hc.apache.org/httpclient-3.x/"&gt;jakarta.commons.httpclient&lt;/a&gt;-et választottam, abból is a &lt;a href="http://xenia.sote.hu/ftp/mirrors/www.apache.org/httpcomponents/commons-httpclient/binary/commons-httpclient-3.1.tar.gz"&gt;3.1&lt;/a&gt;-es verziót.&lt;br /&gt;
&lt;/div&gt;&lt;pre class="brush: java;"&gt;HttpClient httpclient = new HttpClient();
Protocol myhttps = new Protocol("https", new SSLProtocolSocketFactory(), 443);
httpclient.getHostConfiguration().setHost("somehost.tld", 443, myhttps);
GetMethod method = new GetMethod("/some_uri"); //PostMethod to post data

try {
    httpclient.executeMethod(method);
    String response = new String(method.getResponseBody());
} finally {
    method.releaseConnection();
}
&lt;/pre&gt;&lt;div&gt;A kódot futtatva az alábbi Exception-t kaptam:&lt;br /&gt;
&lt;/div&gt;&lt;pre&gt;javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
&lt;/pre&gt;&lt;div&gt;Első teendőnk ebben az esetben, hogy megszerezzük a tanusítvány publikus részét, majd letöltve hozzáadjuk egy kulcs-tartóhoz, az alábbi paranccsal:&lt;br /&gt;
&lt;/div&gt;&lt;pre&gt;keytool -importcert -trustcacerts -alias untrustedCert -file somecert.crt -keystore /usr/lib/jvm/java/jre/lib/security/cacerts
&lt;/pre&gt;&lt;div&gt;Mivel én fejlesztői környezetben szeretnék kapcsolódni, a Java alapértelmezett kulcs-tartójához adtam hozzá a tanusítványt, az alapértelmezett "changeit" jelszó segítségével. Ezek után nincs más dolgunk, mint a JVM-nek átadni paraméterként, hogy a kiszemelt kulcs-tartót fogadja el mindenképpen hitelesnek.&lt;br /&gt;
&lt;/div&gt;&lt;pre&gt;-Djavax.net.ssl.trustStore=/usr/lib/jvm/java/jre/lib/security/cacerts
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-8992260885029170573?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/8992260885029170573/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/04/https-kapcsolodas-self-signed-esvagy.html#comment-form' title='2 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/8992260885029170573'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/8992260885029170573'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/04/https-kapcsolodas-self-signed-esvagy.html' title='HTTPS kapcsolódás self-signed és/vagy lejárt tanusítvánnyal ellátott szolgáltatáshoz'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-7302624575263437300</id><published>2010-04-04T11:51:00.004+02:00</published><updated>2010-04-06T15:56:27.305+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Képek átméretezése, első nekifutás</title><content type='html'>&lt;div&gt;Ebben a témában rengeteg bejegyzés található a neten, én személy szerint mégsem találtam elfogadható megoldás, ami egyben megoldaná minden problémámat, kivéve a &lt;a href="http://en.wikipedia.org/wiki/Java_Advanced_Imaging"&gt;Java Advanced Imaging&lt;/a&gt;-ot. Utóbbival elég nagy problémám volt, hogy nincsen agyondokumentálva, vannak ugyan példák, de azokon eligazodni is felér egy rém-álommal. A rossz tapasztalatok sarkalltak arra, hogy belekezdjek egy, az én igényeimet kielégítő, eszköz megírásába, mely az alapvető webes környezetben előforduló problémákat orvosolja. A projekt elérkezett az első "mérföld-kőhöz", így gondoltam szánok rá pár sort.&lt;/div&gt;&lt;div&gt;Jelenlegi stádiumban képes átméretezni jpg, png, gif, anim gif formátumokat, alkalmazza az élsimítás technológiát, viszont a transzparens képekbe még beletörik a foga. A művelet pilléreit szeretném itt bemutatni, a teljes forráskódot elérhetővé teszem, de mivel felhasználtam pár osztályt, amit letöltöttem és javítottam, ezek pontos liszenszelését még egyeztetem.&lt;/div&gt;Első esetben nézzük az egyszerűbb oldalát a dolognak, amikor nem animált gif-et kell átméretezni. Mivel a legtöbb példa csak a &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Graphics2D.html"&gt;Graphics2D&lt;/a&gt; beépített élsimítását használta fel, aminek minősége finoman szólva is vacak, így átméretezés előtt egy blur effektet húzok a képre. A példa csak a kicsinyítés problémájával foglalkozik, mivel a jelentős minőség-romlás ilyen esetben érzékelhető igazán, és a gyakorlatban is ritkán kell nagyítani egy felhasználó által feltöltött képet.&lt;br /&gt;
&lt;pre class="brush: java;"&gt;OutputStream out;
BufferedImage bufferedImage;
String extension;
//...
if (!extension.equals("gif")) {
//Első lépésként, amenniyben jpg-a forrás kép, át kell állítanunk a típusát, mivel az alapértelmezett típus nem támogatja a blur készítését, minden más esetben, kompatibilissé tesszük a képet, szintén a blur-ozhatóság érdekében.
    if (mimeType.equals("image/jpeg") || mimeType.equals("image/pjpeg")) {
        bufferedImage = convert(bufferedImage, BufferedImage.TYPE_INT_RGB);
    } else {
        bufferedImage = createCompatibleImage(bufferedImage);
    }

//Elvégezzük a blur-ozást, és átméretezést, majd mehet a kép a kimenetre.
    ImageIO.write(resize(blur(bufferedImage), newWidth.intValue(), newHeight.intValue()), extension, out);

    out.flush();
    out.close();
}
&lt;/pre&gt;A meghívott metódusok:&lt;br /&gt;
&lt;pre class="brush: java;"&gt;//Átméretezi a képet.
private BufferedImage resize(BufferedImage image, int width, int height) {
    BufferedImage result = new BufferedImage(width, height, image.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : image.getType());
    Graphics2D g = result.createGraphics();
    g.setComposite(AlphaComposite.Src);
    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g.drawImage(image, 0, 0, width, height, null);
    g.dispose();

    return result;
}

//Blur-ozza a képet.
private BufferedImage blur(BufferedImage image) {
    float ninth = 1.0f / 9.0f;
    float[] blurKernel = {
        ninth, ninth, ninth,
        ninth, ninth, ninth,
        ninth, ninth, ninth
    };

    Map map = new HashMap();
    map.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    map.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    map.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    BufferedImageOp op = new ConvolveOp(new Kernel(3, 3, blurKernel), ConvolveOp.EDGE_NO_OP, new RenderingHints(map));

    return op.filter(image, null);
}

//Átalakítja a típusát a képnek.
private BufferedImage convert(BufferedImage image, int type) {
    BufferedImage result = new BufferedImage(image.getWidth(), image.getHeight(), type);
    Graphics2D g = result.createGraphics();
    g.drawRenderedImage(image, null);
    g.dispose();

    return result;
}

//Kompatibilissé teszi a képet.
private static BufferedImage createCompatibleImage(BufferedImage image) {
    GraphicsConfiguration gc = BufferedImageGraphicsConfig.getConfig(image);
    BufferedImage result = gc.createCompatibleImage(image.getWidth(), image.getHeight(), Transparency.BITMASK);
    Graphics2D g = result.createGraphics();
    g.drawRenderedImage(image, null);
    g.dispose();

    return result;
}
&lt;/pre&gt;A nehezebb eset, mint említettem, animált gifek átméretezése, mivel a Graphics2D nem támogatja alapértelmezetten azokat. A megoldás nem egyedüli érdemem, bár a letöltött kódokat javítanom kellett. A lényeg, hogy a gif kép-kockáin egyesével végig kell iterálni, és az előbb említett műveletek végrehajtása után, a cél képbe beletenni az immár átméretezett példányokat.&lt;br /&gt;
&lt;pre class="brush: java;"&gt;try {
    GifDecoder d = new GifDecoder();
    d.read(image.getAbsolutePath());

    AnimatedGifEncoder e = new AnimatedGifEncoder();
    e.setRepeat(d.getLoopCount());
    e.start(out);

    int type = bufferedImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : bufferedImage.getType();

    for (int i = 0; i &amp;lt; d.getFrameCount(); i++) {
        e.setDelay(d.getDelay(i));
                    
        BufferedImage frameBuffer = new BufferedImage(origWidth, origHeight, BufferedImage.TYPE_BYTE_INDEXED);
        frameBuffer.getGraphics().drawImage(d.getFrame(i), 0, 0, null);

        if (mimeType.equals("image/jpeg") || mimeType.equals("image/pjpeg")) {
            frameBuffer = convert(frameBuffer, BufferedImage.TYPE_INT_RGB);
        } else {
            frameBuffer = createCompatibleImage(frameBuffer);
        }

        e.addFrame(resize(blur(frameBuffer), newWidth.intValue(), newHeight.intValue()));
    }

    e.finish();
    out.flush();
    out.close();
} catch (Exception e) {}
&lt;/pre&gt;&lt;div&gt;A javított osztályokat egyenlőre&lt;a href="http://pastebin.com/"&gt; pastebin.com&lt;/a&gt;-ra töltöttem fel.&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://pastebin.com/D51899KG"&gt;AnimatedGifEncoder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://pastebin.com/Pi7mLCda"&gt;GifDecoder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://pastebin.com/4GMA8BXR"&gt;LZWEncoder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://pastebin.com/TAnNpuux"&gt;NeuQuant&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;Nem állítom, hogy ez a legjobb és legszebb megoldás, és azt sem, hogy tökéletes eredményt hoz minden típusú képnél, csak azt tudom, hogy a transzparens képek kivételével eddig bevált.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-7302624575263437300?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/7302624575263437300/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/04/kepek-atmeretezese-elso-nekifutas.html#comment-form' title='0 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/7302624575263437300'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/7302624575263437300'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/04/kepek-atmeretezese-elso-nekifutas.html' title='Képek átméretezése, első nekifutás'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-4375922373364615417</id><published>2010-03-23T16:22:00.010+01:00</published><updated>2010-03-23T16:53:01.520+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='EJB'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='J2EE'/><title type='text'>Egységesített "Global JNDI name" kezelés EJB 3.1 környezetben</title><content type='html'>&lt;div&gt;Az alkalmazás-szerver, amikor létrehozunk egy Session Bean-t, automatikusan regisztrálja azt a JNDI kontextusban. Az regisztrált Bean-ekre referenciát legegyszerűbben a függőség-injektálással szerezhetünk, a szerver leveszi a direkt név-feloldás terhét vállunkról. Az említett módszer egységesen működik minden alkalmazás-szerveren, viszont a megvalósítása már korántsem egységes. Minden gyártó saját elképzelése szerint oldotta meg a feladatot.&lt;/div&gt;&lt;ul&gt;&lt;li&gt;JBoss global JNDI name: ejbapp/BeanName/local&lt;/li&gt;
&lt;li&gt;GlassFish global JNDI name: InterfaceClass&lt;/li&gt;
&lt;li&gt;WebSphere Community Edition global JNDI name: ejbapp-ejb/BeanName/InterfaceClass&lt;/li&gt;
&lt;li&gt;Oracle Application Server (OC4J) global JNDI name: BeanName&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;Mivel az alkalmazás-szerver elrejti előlünk a feloldás problémáját, egészen addig amíg van lehetőségünk a függőség-injektálásra támaszkodni, kódunk hordozható marad. Viszont abban a pillanatban, amikor pl. egy nem menedzselt objektumból, vagy egy unit-tesztelőből kényszerülünk direkt JNDI név-feloldásra, kénytelenek vagyunk gyártó specifikus kódot készíteni.&lt;/div&gt;&lt;div&gt;Egy egyszerű példa szemlélteti a problémát EJB 3 környezetben:&lt;/div&gt;&lt;pre class="brush: java"&gt;@Remote
public interface JNDITestBeanInterface {
    String test();
}
&lt;/pre&gt;&lt;pre class="brush: java"&gt;@Stateless(name="JNDITestBean")
public class JNDITestBean implements JNDITestBeanInterface {
    public String test() {
        return "ok";
    }
}
&lt;/pre&gt;&lt;pre class="brush: java"&gt;public class JNDITestBeanClient {
    public static void main(String[] args) throws Exception {
        Context context = new InitialContext();
        JNDITestBeanInterface testBean = (JNDITestBeanInterface) context.lookup("JNDITest/JNDITestBeanInterface/remote");
        System.out.print(testBean.test());
    }
}
&lt;/pre&gt;&lt;div&gt;Az EJB 3.1 specifikáció megoldást kínál, mégpedig úgy, hogy egységesíti a név-regisztráció módját, így azon alkalmazás-szerverek, melyek implementálni szeretnék a specifikációt kénytelenek követni is azt.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;java:global[/&amp;lt;application-name&amp;gt;]/&amp;lt;module-name&amp;gt;/&amp;lt;bean-name&amp;gt;#&amp;lt;interface-name&amp;gt;&lt;/b&gt;&lt;br /&gt;
&lt;/div&gt;&lt;pre class="brush: java"&gt;@Stateless
@LocalBean
public class JNDITestBean {
    public String test() {
        return "ok";
    }
}
&lt;/pre&gt;&lt;pre class="brush: java"&gt;public class JNDITestBeanClient {
    public static void main(String[] args) throws Exception {
        Context context = new InitialContext();
        JNDITestBean testBean = (JNDITestBean) context.lookup("java:global/JNDITest/JNDITest-ejb/JNDITestBean");
        System.out.print(testBean.test());
    }
}
&lt;/pre&gt;&lt;div&gt;Az "interface-name" elhagyható, amenniyben interface nélküli Bean-re szeretnénk referenciát szerezni, továbbá ha az egyszerűsített deployment technológiát használva a WAR-ba helyeztük az EJB réteget az "application-name" azaz az alkalmazás neve opcionálissá válik.&lt;br /&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-4375922373364615417?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='enclosure' type='' href='http://www.theserverside.com/news/1363649/New-Features-in-EJB-31-Part-5' length='0'/><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/4375922373364615417/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/03/egysegesitett-global-jndi-name-kezeles.html#comment-form' title='0 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/4375922373364615417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/4375922373364615417'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/03/egysegesitett-global-jndi-name-kezeles.html' title='Egységesített &quot;Global JNDI name&quot; kezelés EJB 3.1 környezetben'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-67485169373470570</id><published>2010-03-17T11:02:00.002+01:00</published><updated>2010-03-17T11:06:39.959+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Database'/><title type='text'>Cassandra</title><content type='html'>&lt;div&gt;Több irányból is érkeznek a hírek, hogy nagyobb tartalom-szolgáltatók sorra lecserélik adatbázis megoldásaikat &lt;a href="http://cassandra.apache.org/"&gt;Cassandrára&lt;/a&gt;. A Cassandra program arany fokozatú Apache projektté nőtte ki magát, melyet kezdetben a Facebook fejlesztett és tett közzé nyílt forrással. Az adatbázis egy jól skálázható, második generációs, strukturált kulcs-érték tárolására alkalmas Java nyelven írt eszköz, amit kifejezetten több-gépes, clusterezett rendszerekre terveztek.&lt;br /&gt;
Néhány alap fogalom:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Column: Az oszlop, a legkisebb egysége az adat-tárolásnak, melyben kulcs, érték,és egy idő-bélyeg található.&lt;/li&gt;
&lt;li&gt;ColumnFamily: Adattábla, mely az oszlopokat fogja össze.&lt;/li&gt;
&lt;li&gt;SuperColumns: Olyan oszlopokat tartalmaz, melyek értékei további oszlopok.&lt;/li&gt;
&lt;li&gt;Row: Minden adattábla elszeparált fájlban tárolódik, és ezek a fájlok tárolódnak az un.: Row-ban. Minden Row-nak van egy kulcsa, amivel hivatkozni lehet rá.&lt;/li&gt;
&lt;li&gt;Keyspace: Lényegében az adatbázis név-tér az adattábláknak, tipikusan egy alkalmazáshoz egy név-tér tartozik.&lt;/li&gt;
&lt;li&gt;Cluster: A cluster kezeli az elosztott név-tereket.&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;Ez az egyszerű JSON példa jól szemlélteti a Row, ColumnFamily, és Column struktúráját:&lt;/div&gt;&lt;/div&gt;&lt;pre class="brush: javascript"&gt;{
   "mccv":{
      "Users":{
         "emailAddress":{"name":"emailAddress", "value":"foo@bar.com"},
         "webSite":{"name":"webSite", "value":"http://bar.com"}
      },
      "Stats":{
         "visits":{"name":"visits", "value":"243"}
      }
   },
   "user2":{
      "Users":{
         "emailAddress":{"name":"emailAddress", "value":"user2@bar.com"},
         "twitter":{"name":"twitter", "value":"user2"}
      }
   }
}
&lt;/pre&gt;&lt;div&gt;Az adat-szerkezet részletesebb leírása megtalálható &lt;a href="http://wiki.apache.org/cassandra/DataModel"&gt;itt&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;Fontos különbség a relációs adatbázisokhoz képest, hogy míg utóbbiakban az entitások és a közöttük fennálló kapcsolat alapján keresünk, és a keresést index-ek tesszük élhetővé, addig a Cassandra fordított paradigmát igényel, és a tervezésnél azt kell figyelembe vennünk, hogy milyen lekérdezéseket szeretnénk kiszolgálni, mert mert egy adattábla lényegében egy lekérdezésnek "felel meg".&lt;/div&gt;&lt;div&gt;Nézzük, hogyan is működik.&lt;br /&gt;
A telepítést részletesen leírja a &lt;a href="http://wiki.apache.org/cassandra/FrontPage"&gt;wiki&lt;/a&gt;, ezért arra nem térnék ki. Miután sikeresen feltelepítettük a lánykát a conf/storage-conf.xml állományban tudjuk hangolni a rendszert, illetve itt van lehetőség Keyspace-k és ColumnFamily-k létrehozására, az alábbi módon.&lt;/div&gt;&lt;pre class="brush: xml"&gt;&amp;lt;Keyspaces&amp;gt;
    &amp;lt;Keyspace Name="testKeyspace"&amp;gt;
        &amp;lt;KeysCachedFraction&amp;gt;0.01&amp;lt;/KeysCachedFraction&amp;gt;
        &amp;lt;ColumnFamily CompareWith="UTF8Type" Name="testColumnFamily"/&amp;gt;
    &amp;lt;/Keyspace&amp;gt;
&amp;lt;/Keyspaces&amp;gt;
&lt;/pre&gt;&lt;div&gt;Mentsük el az XML-t indítsuk el a Cassandrát, majd a "cassandra-cli -host localhost -port 9160" paranccsal csatlakozzunk az adatbázishoz. Ha minden jól ment a cassandra&amp;gt; felirat jelenik meg a képernyőn jelezve, ahol is megkezdhetjük a munkát. Első kérésként ellenőrizzük, hogy sikeresen létre hoztuk-e a Keyspace-t. &lt;br /&gt;
&lt;pre&gt;cassandra&amp;gt;show keyspaces
testKeyspace
system
&lt;/pre&gt;Ne lepődjünk meg van egy system Keyspace, amit a rendszer maga használ. Jelen esetben létrehoztuk egy testColumnFamily-t amiben az adatokat szeretnénk tárolni UTF-8 string formában, de van mód ASCIIType, BytesType, TimeUUIDType, Super tárolási formákra.&lt;/div&gt;&lt;div&gt;Következő lépésként helyezzünk be egy Column-ot a familybe.&lt;br /&gt;
&lt;pre&gt;cassandra&amp;gt;set testKeyspace.testColumnFamily ['testUser']['foo'] = 'bar'
Value inserted.
&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;És végül kérdezzük le a felvitt értéket.&lt;br /&gt;
&lt;pre&gt;cassandra&amp;gt;get testKeyspace.testColumnFamily ['testUser']['foo']
=&amp;gt; (column=foo, value=bar, timestamp=1268818855201)
&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;Mivel leggyakrabban nem parancssorból szeretnénk használni az adatbázist, számtalan programozási nyelvhez létezik már implementáció, köztük természetesen &lt;a href="http://code.google.com/p/cassandra-java-client/"&gt;Java&lt;/a&gt;-hoz is. Az említett kliensben az előbbi példa e-képpen valósítható meg:&lt;br /&gt;
&lt;pre class="brush: java"&gt;CassandraClient cl = pool.getClient();
KeySpace ks = cl.getKeySpace("testKeyspace");

//insert value
ColumnPath cp = new ColumnPath("testColumnFamily" , null, "testUser".getBytes("utf-8"));
ks.insert("foo", cp , "bar".getBytes("utf-8"));

//get value
Column col = ks.getColumn("foo", cp);
String value = new String(col.getValue(),"utf-8");

pool.releaseClient(cl);
pool.close();
&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;Összegzésként elmondható, hogy Cassandra sem váltja meg a világot, mégis megvan a maga helye és szerepe az adatbázisok területén. Lényeges működésbeli sajátossága hátrány, ha bonyolult adastruktúrákban gondolkozunk, viszont előnye, hogy rendkívül gyorsan képes kiszolgálni a kéréseket. Véleményem szerint relációs adatbázissal vegyesen, mindkettő erősségét (ki)használva optimális megoldáshoz jutunk. &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-67485169373470570?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='enclosure' type='' href='http://cassandra.apache.org' length='0'/><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/67485169373470570/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/03/cassandra.html#comment-form' title='0 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/67485169373470570'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/67485169373470570'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/03/cassandra.html' title='Cassandra'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-225029756262530993</id><published>2010-03-09T08:18:00.014+01:00</published><updated>2010-03-12T14:00:16.683+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='EJB'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='J2EE'/><title type='text'>JRebel, avagy hogyan tegyük egyszerűbbé az EEletet</title><content type='html'>Bár elkötelezett híve vagyok a nyílt forrású szoftverfejlesztésnek,a címben említett eszköz mellett mégsem bírtam elmenni csukott szemmel. Mentségükre legyen mondva, a fizetős terméküket bármely szabad szoftver fejlesztéséhez &lt;a href="http://www.zeroturnaround.com/blog/javarebel-for-open-source-development/"&gt;ingyen biztosítják&lt;/a&gt;.  A &lt;a href="http://www.zeroturnaround.com/"&gt;JRebel&lt;/a&gt; projekt célja, hogy olyan megvalósításokkal támogassa meg a fejlesztőket, melyek nem része a JVM-nek, így hagyományos módon vagy sehogy, vagy igen körülményesen oldhatóak csak meg. A fejlesztők 5 fő pontot neveznek meg, melyek az alábbiak:&lt;div&gt;&lt;div&gt;1. &lt;b&gt;Új osztályok hozzáadása&lt;/b&gt;&lt;/div&gt;&lt;div&gt;A HotSwap protokoll csak meglévő osztályok frissítését tesz lehetővé futás-időben, mivel egy egyedi megfeleltetés van az osztály neve és tartalma között. Ahhoz, hogy új osztályt adhassunk hozzá, kell válasszunk egy class loadert, mivel minden osztály egy specializált loaderben töltődik be. Sajnos ezt a szolgáltatást nem támogatja a HotSwap.&lt;/div&gt;&lt;div&gt;2. &lt;b&gt;Erő-forrás-állományok frissítése&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Az osztályaink mellé gyakran teszünk erő-forrás-állományokat melyek leggyakrabban xml vagy properties fájlok. A HotSwap technológia nem támogatja ezen állományok dinamikus módosítását, azaz minden változtatást deployolni kell a szerverre. Ez elég nagy probléma, főleg ha a szerveren van már pl. feltöltött állomány, mert a deploy folyamat esetlegesen törli azokat.&lt;/div&gt;&lt;div&gt;3. &lt;b&gt;Webes erő-források&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Minden weben megjelenő alkalmazásban vannak képek, HTML, CSS, JS, stb állományok, melyek az előző ponthoz hasonlóan nem frissülnek automatikusan a web-konténerben.&lt;/div&gt;&lt;div&gt;4. &lt;b&gt;Gyorsító-tárak&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Enterprise környezet nagy előnye, hogy a konténer, legyen szó web-konténerről vagy alkalmazás-konténerről, gyorsító-tárrazza osztályainkat. Ez ez előny egyben egy hátrány is, hiszen új elemek, pl. metódusok hozzáadása után a tár törléséig az előző állapot érhető el. Megjegyzem a legtöbb IDE támogatja a "deploy on save" metódust, mely az osztály mentésekor automatikusan frissíti a szerveren az osztályt.&lt;/div&gt;&lt;div&gt;5. &lt;b&gt;Menedzselt komponensek&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Egy alkalmazásban számtalan un. menedzselt komponens (EJB-k, Spring bean-ek, stb.) vannak. Az ilyen osztályokra jellemző, hogy általában van függőség-injektálással a konténer által behelyezett osztály, némely állapota társítva van un. instance vagy instance pool-lal, a funkcionalitás megvalósítása különböző rétegeken történik. Frissítésénél nem elegendő magát az osztályt frissíteni, hanem a különböző rétegeket, injectált osztályokat is.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;Mindezen problémák feloldására a JRebel egy sajátságos metodikát ajánl, amit egy egyszerű, mégis hétköznapi példán mutatnék be. Jelenleg a 2.2.1-es verzió a legfrissebb, amit a &lt;a href="http://www.zeroturnaround.com/jrebel/current/"&gt;letöltés oldal&lt;/a&gt;ról szerezhetünk meg, 30 napos próbára. A telepítés grafikusan történik pár lépésben. A telepítő felajánlja, hogy befejezés után futtatja is a varázslót, amely megjegyzem példásan kivitelezett alkalmazás, minek segítségével pofon egyszerűvé válik a konfigurálás. A varázslót "UNIX like" környezetben a bin/jrebel-config.sh script futtatásával "varázsolhatjuk" elő. &lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Nincs más dolgunk mint első lépésben a megfelelő IDE-t kiválasztani, én Netbeans-el próbálkoztam, de támogatott az Eclipse, IntelliJ is.&lt;/li&gt;
&lt;li&gt;Második lépésben felajánlja a varázsló az IDE-knek megfelelő plugint, Netbeans esetén a beépített pluginek közül a legegyszerűbb beszerezni.&lt;/li&gt;
&lt;li&gt;Következő, sorban a harmadik lépés, hogy alkalmazásunkat felkészítsük a JRebel használatára. A segédlet mindent érthetően elmagyaráz, EJB projekt esetén a források gyökerébe, web projekt esetén a WEB-INF/classes mappában kell egy-egy rebel.xml állományt elhelyeznünk, és az xml-ben specifikálni, hogy mely osztályokat és/vagy erő-forrásokat szeretnénk ha a JRebel kezelné. Itt térnék ki kicsit a működésre. A JRebel egy újabb rétegként rátelepszik az alkalmzásunkra, és a betöltendő osztályokat vagy erő-forrásokat a rebel.xml alapján közvetlenül a fejlesztőkörnyezetből tölti be. Biztos mindenkinek volt már problémája a dinamikusan fordított web-oldalakkal, amikor is a WEB-INF/classes-ban elhelyezett translate.properties-t minden apró változtatás után deployolni kellett. Mivel ez egy gyakori eset, ebből a problémából indultam ki.&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;pre class="brush: xml;"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;application
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.zeroturnaround.com"
xsi:schemaLocation="http://www.zeroturnaround.com http://www.zeroturnaround.com/alderaan/rebel-2_0.xsd"&amp;gt;
&amp;lt;web&amp;gt;
&amp;lt;link target="/"&amp;gt;&amp;lt;dir name="~/NetBeansProjects/webapp/webapp-war/web"/&amp;gt;&amp;lt;/link&amp;gt;
&amp;lt;/web&amp;gt;
&amp;lt;/application&amp;gt;
&lt;/pre&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Negyedik lépéséként választanunk kell Java verziót, operációs rendszert, és konténert, utóbbiból eléggé széles a paletta, én Glassfish 3-at használok, ezért azt választottam. Glassfish esetén az adminisztrációs felületen a JVM Setting / JVM Options alatt 2 paramétert kell megadnunk a JVM-nek, mégpedig a -noverify és -javaagent:/utvonal/jrebel.jar.&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Ötödik lépésben az ügynök, na jó az agent beállításait kell megadnunk. Az advanced gombra kattintva állíthatjuk be pl. azt, ha nem akarunk a rebel.xml-ben abszolút eléréseket megadni, ennek módját részletesen tárgyalja a varázsló. A beállító-panelt "UNIX like" környezetben a bin/agent-settings.sh script futtatásával indíthatjuk.&lt;/li&gt;
&lt;li&gt;Nincs más dolgunk, mint újraindítani az alkalmazás-szervert, deployolni a rebel.xml-el megfejelt alkalmazásunkat, és máris élvezhetjük a produktumot.&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;Bár a JRebel fizetős eszköz, a honlapján elhelyezett &lt;a href="http://www.zeroturnaround.com/jrebel/value-calculator/"&gt;kalkulátor&lt;/a&gt; segítségével gyorsan kiszámolható megéri-e az árát, viszont a pénzben nem mérhető könnyebbséget még a kalkulátor sem tudja számításba venni.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-225029756262530993?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/225029756262530993/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/03/jrebel-avagy-hogyan-tegyuk-egyszerubbe.html#comment-form' title='1 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/225029756262530993'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/225029756262530993'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/03/jrebel-avagy-hogyan-tegyuk-egyszerubbe.html' title='JRebel, avagy hogyan tegyük egyszerűbbé az EEletet'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-4873266773171234755</id><published>2010-03-02T11:01:00.010+01:00</published><updated>2010-03-12T14:29:45.485+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='EJB'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='J2EE'/><title type='text'>EJB 3.1 Lite</title><content type='html'>&lt;div&gt;A Java EE 6 megjelenése számos újdonságot hozott, már &lt;a href="http://jpattern.blogspot.com/2010/02/aszinkron-ejb-31-hivas.html"&gt;itt&lt;/a&gt; is volt szó róla, ezek közül egy érdekesség az un. web-profilok létrehozása. Mint EE fejlesztő gyakran megfordult már a fejemben, hogy vajon tényleg mindig szükség van a teljes enterprise környezetre, hogy vajon nem-e ágyúval verébre amit csinálok, hiszen a szolgáltatások tárházának töredék részét használom egy-egy projektben. Az érem másik oldalán viszont mindig ott van az egyszerű fejlesztés, az átlátható logika (amit egy kicsit az architektúra is kikényszerít), valamint az élesen elkülönített szerep-körök. Ezt a problémát gondolták orvosolni az EE 6 fejlesztői a web-profilok segítségével, mégpedig úgy, hogy az alkalmazásunkhoz igazíthatjuk a felhasznált és szállított rétegeket. A szintén újdonságként megjelenő egyszerűsített deployment technológiának köszönhetően, melynek lényege, hogy az ejb-réteg behelyezhető a web-rétegbe megspórolva a két különálló projektre, némi megkötéssel egy web-konténer, is képes futtatni enterprise alkalmazásunkat. Az EJB Lite technológia nem más, mint egy előre csomagolt web-profil, az alábbi kritériumokkal:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span"   style="  ;font-family:'Times New Roman';font-size:medium;"&gt;Stateless, Stateful, és Singleton session beaneket használhatunk&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="Apple-style-span"   style="  ;font-family:'Times New Roman';font-size:medium;"&gt;csak lokális EJB hívásokat végezhetünk&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="Apple-style-span"   style="  ;font-family:'Times New Roman';font-size:medium;"&gt;nem támogatja az aszinkron hívásokat&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="Apple-style-span"   style="  ;font-family:'Times New Roman';font-size:medium;"&gt;támogatja a deklaratív és programozott tranzakciókat&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;A csomag egy annyira "lebutított" profil, hogy használatához web-konténer sem kell, ennek köszönhetően bármely hagyományos alkalmazásunkban használható, mindössze a &lt;a href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/ejb/package-summary.html"&gt;javax.ebj&lt;/a&gt; és glassfish-embedded-static-shell könyvtár-gyüjteményt (jar) kell importálnunk.&lt;/div&gt;&lt;pre class="brush: java;"&gt;import javax.ejb.Stateless;

@Stateless
public class TestBean {
public String test() {
   return "ok";
}
}
&lt;/pre&gt;&lt;pre class="brush: java"&gt;import java.util.Properties;
import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;
import javax.naming.NamingException;

public class Main {
public static void main(String[] args) throws NamingException {
   Properties properties = new Properties();
   properties.put("org.glassfish.ejb.embedded.glassfish.installation.root", "");
   EJBContainer container = EJBContainer.createEJBContainer(properties);
   Context namingContext = container.getContext();

   TestBean testBean = (TestBean) namingContext.lookup("java:global/classes/TestBean");

   System.out.println(testBean.test());
}
}
&lt;/pre&gt;Az alkalmazást futtatva az alábbi kimenetet kapjuk: &lt;pre&gt;Mar 3, 2010 8:47:28 AM org.glassfish.ejb.embedded.EJBContainerProviderImpl getValidFile
SEVERE: ejb.embedded.location_not_exists
Mar 3, 2010 8:47:31 AM com.sun.enterprise.v3.server.AppServerStartup run
INFO: GlassFish v3 (74.2) startup time : Embedded(2591ms) startup services(492ms) total(3083ms)
Mar 3, 2010 8:47:31 AM org.glassfish.admin.mbeanserver.JMXStartupService$JMXConnectorsStarterThread run
INFO: JMXStartupService: JMXConnector system is disabled, skipping.
Mar 3, 2010 8:47:31 AM com.sun.enterprise.transaction.JavaEETransactionManagerSimplified initDelegates
INFO: Using com.sun.enterprise.transaction.jts.JavaEETransactionManagerJTSDelegate as the delegate
Mar 3, 2010 8:47:31 AM AppServerStartup run
INFO: [Thread[GlassFish Kernel Main Thread,5,main]] started
Mar 3, 2010 8:47:32 AM com.sun.appserv.connectors.internal.api.ConnectorsUtil extractRar
INFO: could not find RAR [ __ds_jdbc_ra.rar ] in the archive, skipping .rar extraction
Mar 3, 2010 8:47:32 AM com.sun.appserv.connectors.internal.api.ConnectorsUtil extractRar
INFO: could not find RAR [ __cp_jdbc_ra.rar ] in the archive, skipping .rar extraction
Mar 3, 2010 8:47:32 AM com.sun.appserv.connectors.internal.api.ConnectorsUtil extractRar
INFO: could not find RAR [ __xa_jdbc_ra.rar ] in the archive, skipping .rar extraction
Mar 3, 2010 8:47:32 AM com.sun.appserv.connectors.internal.api.ConnectorsUtil extractRar
INFO: could not find RAR [ __dm_jdbc_ra.rar ] in the archive, skipping .rar extraction
Mar 3, 2010 8:47:32 AM com.sun.enterprise.security.SecurityLifecycle &lt;init&gt;
INFO: security.secmgroff
Mar 3, 2010 8:47:32 AM com.sun.enterprise.security.ssl.SSLUtils checkCertificateDates
SEVERE: java_security.expired_certificate
Mar 3, 2010 8:47:32 AM com.sun.enterprise.security.SecurityLifecycle onInitialization
INFO: Security startup service called
Mar 3, 2010 8:47:32 AM com.sun.enterprise.security.PolicyLoader loadPolicy
INFO: policy.loading
Mar 3, 2010 8:47:33 AM com.sun.enterprise.security.auth.realm.Realm doInstantiate
INFO: Realm admin-realm of classtype com.sun.enterprise.security.auth.realm.file.FileRealm successfully created.
Mar 3, 2010 8:47:33 AM com.sun.enterprise.security.auth.realm.Realm doInstantiate
INFO: Realm file of classtype com.sun.enterprise.security.auth.realm.file.FileRealm successfully created.
Mar 3, 2010 8:47:33 AM com.sun.enterprise.security.auth.realm.Realm doInstantiate
INFO: Realm certificate of classtype com.sun.enterprise.security.auth.realm.certificate.CertificateRealm successfully created.
Mar 3, 2010 8:47:33 AM com.sun.enterprise.security.SecurityLifecycle onInitialization
INFO: Security service(s) started successfully....
Mar 3, 2010 8:47:33 AM com.sun.ejb.containers.BaseContainer initializeHome
INFO: Portable JNDI names for EJB TestBean : [java:global/classes/TestBean, java:global/classes/TestBean!ejblite.TestBean]
ok
&lt;/init&gt;&lt;/pre&gt;&lt;div&gt;Bár nem hiszem, hogy a technológia leggyakoribb alkalmazási területét sikerült megragadnom, mégis ezen egyszerű példából kiindulva csak a fejlesztő kreativitásán múlik, hogy milyen környezetbe ágyazva alkalmazza az EJB réteget.&lt;/div&gt;&lt;div&gt;Aki részletesebben szeretne tájékozódni a témában, megteheti &lt;a href="http://weblogs.java.net/blog/robc/archive/2008/02/profiles_in_the_1.html"&gt;Roberto Chinnici&lt;/a&gt; blogjában, vagy a &lt;a href="http://www.theserverside.com/tt/articles/article.tss?l=NewFeaturesEJB31-3"&gt;TheServerSide&lt;/a&gt; portálon. Az egyszerűsített deploymentről &lt;a href="http://www.theserverside.com/tt/articles/article.tss?l=NewFeaturesEJB31"&gt;ebben a cikkben&lt;/a&gt; található részletesebb leírás. &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-4873266773171234755?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/4873266773171234755/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/03/ejb-31-lite.html#comment-form' title='0 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/4873266773171234755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/4873266773171234755'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/03/ejb-31-lite.html' title='EJB 3.1 Lite'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-3686151627719726186</id><published>2010-02-25T08:50:00.011+01:00</published><updated>2011-12-15T22:48:40.492+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='JSON'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='AJAX'/><title type='text'>JavaScript EE, JSON válaszol</title><content type='html'>Előző &lt;a href="http://jpattern.blogspot.com/2010/02/javascript-ee-izelito-javascript.html"&gt;bejegyzésben&lt;/a&gt; bemutattam miként lehet Java környezetünket rávenni JavaScript futtatására. Folytatásban egy &lt;a href="http://java.sun.com/products/servlet/"&gt;servlet&lt;/a&gt;-en keresztül rávesszük a &lt;a href="http://en.wikipedia.org/wiki/Web_container"&gt;web-konténert&lt;/a&gt;, hogy &lt;a href="http://www.json.org/"&gt;JSON&lt;/a&gt; válaszokat adjon a kérésekre.&lt;div&gt;Legelső lépésként egy hagyományos servletet kell létrehoznunk, majd a doGet metódusában megvalósítani a JavaScript futtatását.&lt;/div&gt;Java kód: &lt;pre class="brush: java;"&gt;Compilable scriptEngine = (Compilable) new ScriptEngineManager().getEngineByName("JavaScript");

ScriptContext scriptContext = new SimpleScriptContext();
scriptContext.setWriter(response.getWriter());
scriptContext.setAttribute("config", getServletConfig(), ScriptContext.ENGINE_SCOPE);
scriptContext.setAttribute("response", response, ScriptContext.ENGINE_SCOPE);
scriptContext.setReader(new InputStreamReader(JSONServlet.class.getResourceAsStream("json.js")));

try {
 CompiledScript script = scriptEngine.compile(scriptContext.getReader());
 script.eval(scriptContext);
} catch (ScriptException e) {}
&lt;/pre&gt;&lt;div&gt;Látható &lt;a href="http://jpattern.blogspot.com/2010/02/javascript-ee-izelito-javascript.html"&gt;különbség&lt;/a&gt;, hogy &lt;a href="http://java.sun.com/javase/6/docs/api/javax/script/ScriptEngine.html"&gt;ScriptEngine&lt;/a&gt; helyett &lt;a href="http://java.sun.com/javase/6/docs/api/javax/script/Compilable.html"&gt;Compilable&lt;/a&gt; osztályt használunk. A Compilable interfészt megvalósító értelmező annyival nyújt több szolgáltatást, hogy a lefordított JavaScripteket újra fel lehet használni újrafordítás nélkül, így segítségével gyorsító-tárazni lehet a scipteket csökkentve a szerver terhelését. Következő lépésként, mivel most nem a JavaScript visszatérési értékére van szükségünk (és azt sem szeretnénk, hogy az alapértelmezett kimenetre írjon), hanem azt szeretnénk, hogy a JavaScript közvetlenül a servlet kimenetre írjon, példányosítanunk kell egy &lt;a href="http://java.sun.com/javase/6/docs/api/javax/script/ScriptContext.html"&gt;ScriptContext&lt;/a&gt; objektumot, és referenciát adnunk a &lt;a href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/ServletResponse.html"&gt;ServletResponse&lt;/a&gt;-ra.  Természetesen van lehetőségünk Java objektumok átadására a JavaScriptnek, de ebben az esetben a ScriptContext osztálynak közvetlenül adhatjuk át a paramétereket a setAttribute metódus meghívásával. Ezután nincs más dolgunk, mint megadni a JavaScript fájlunkat, és futtatni a scriptet.&lt;/div&gt;JavaScript kód: &lt;pre class="brush: javascript"&gt;response.setHeader("Cache-Control", "no-cache");
response.setContentType("application/json");

var json = {
response: {
  status: response.getStatus(),
  content_type: String(response.getContentType())
},
config: {
  servlet: String(config.getServletName())
}
};

println(json.toSource());
&lt;/pre&gt;&lt;div&gt;A scriptben jól látható miként használjuk a Java objektumokat, és végül miként írjunk a kimenetre. &lt;/div&gt;A böngészőben az alábbi válasz jelenik meg: &lt;pre&gt;({response:{status:200, content_type:"text/plain;charset=ISO-8859-1"}, config:{servlet:"JSONServlet"}})
&lt;/pre&gt;&lt;div&gt;Folyt köv... &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-3686151627719726186?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/3686151627719726186/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/02/javascript-ee-json-valaszol.html#comment-form' title='2 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/3686151627719726186'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/3686151627719726186'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/02/javascript-ee-json-valaszol.html' title='JavaScript EE, JSON válaszol'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-5337232245189927980</id><published>2010-02-19T17:40:00.033+01:00</published><updated>2010-03-12T14:11:04.490+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>JavaScript EE, első felvonás</title><content type='html'>&lt;div&gt;Bár a téma nem új-keletű, én most jutottam el oda, hogy legalább kipróbálás szinten foglalkozzam vele. Java SE 6-os verziója óta megtalálható benne a &lt;a href="http://www.mozilla.org/rhino/"&gt;Mozilla Rhinó&lt;/a&gt;-ja, amely egy JavaScript értelmező, és segítségével a JVM-en lehetséges JavaScript kódok futtatása Java kódból. Nincs más dolgunk, mint importálni a &lt;a href="http://java.sun.com/javase/6/docs/api/javax/script/package-summary.html"&gt;javax.script&lt;/a&gt; csomagot, és a &lt;a href="http://java.sun.com/javase/6/docs/api/javax/script/ScriptEngineManager.html"&gt;ScriptEngineManager&lt;/a&gt; segítségével példányosítani egy &lt;a href="http://java.sun.com/javase/6/docs/api/javax/script/ScriptEngine.html"&gt;ScriptEngine&lt;/a&gt; értelmezőt. &lt;/div&gt;&lt;pre class="brush: java;"&gt;ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
&lt;/pre&gt;Java kód: &lt;pre class="brush: java;"&gt;import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.StringBuilder;
import javax.script.*;

public class SimpleJavaScriptTest {
public static void main(String[] args) throws Exception {
//Get reference to the engine
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
//Bind some Java object to the script
Bindings vars = new SimpleBindings();
vars.put("testString", "This is a test string.");
StringBuilder stringBuilder = new StringBuilder();
vars.put("testStringBuilder", stringBuilder);
//Read the JavaScript code
Reader scriptReader = new InputStreamReader(SimpleJavaScriptTest.class.getResourceAsStream("test.js"));
try {
//Run the JavaScript
engine.eval(scriptReader, vars);

System.out.println(stringBuilder.toString());
System.out.println(vars.get("testFile").getClass());
System.out.println(vars.get("jsObject").getClass());
} finally {
scriptReader.close();
}
}
}
&lt;/pre&gt;JavaScript kód: &lt;pre class="brush: javascript"&gt;function printType(obj) {
if (obj.getClass) println("Java object: " + obj.getClass().name);
else println("JS object: " + obj.toSource());
}
printType(testString);

printType(testStringBuilder);
testStringBuilder.append("This content added by JavaScript.");

var testFile = new java.io.File("/");
printType(testFile);

var jsObject = {x: 1, y: { u: 2, v: 3 }};
printType(jsObject);
&lt;/pre&gt;A példát futtatva az alábbi eredményt kapjuk: &lt;pre&gt;JS object: (new String("This is a test string."))
Java object: java.lang.StringBuilderJava object: java.io.File JS object: ({x:1, y:{u:2, v:3}})&lt;/pre&gt;&lt;pre&gt;This content added by JavaScript.
class java.io.File
class sun.org.mozilla.javascript.internal.NativeObject
&lt;/pre&gt;&lt;div&gt;Mint az jól lát-szódik szinte teljes az átjárás a két kód között, bemenő paramétert megfelelő JavaScript típusra cast-olja az értelmező, ennek sikertelensége esetén Java objektumként viselkedik, annak minden funkciójával. Továbbmenve JavaScript kódunkban is van lehetőség Java objektumok létrehozására, melyeket a script futása után elkérhetünk az értelmezőtől. Amennyiben olyan változót kapunk a scriptből, amit Java oldalon nem lehet megfeleltetni egyik osztálynak sem, akkor azzal egy dolgot tehetünk, paraméterként egy másik JavaScriptnek átadhatjuk. &lt;/div&gt;&lt;div&gt;Ezen a ponton merülhet fel a kérdés, hogy valójában ez mire is való? A válasz erre a kérdésre eléggé összetett, és nem is célom maradéktalanul körbejárni. Egyrészt ezzel a módszerrel tudjuk ötvözni a két nyelv tulajdonságait, tehát a Java robusztusságát a JavaScript egyszerűségével összepárosítva kiszélesedik a programozói paletta. Megjegyzem erre a célra nem csak JavaScriptet használhatunk, akár &lt;a href="http://groovy.codehaus.org/"&gt;Groovie&lt;/a&gt;, vagy &lt;a href="http://www.scala-lang.org/"&gt;Scala&lt;/a&gt; kódot is integrálhatunk Java programunkba, ráadásul utóbbiak Java bytecode-ra fordulnak, ami mélyebb integrációra ad lehetőséget. Másrészt kliens oldali scriptjeinket is futtathatjuk server oldalon, ami azt jelenti, hogy nem kell két helyen, két környezetben implementálnunk az azonos működést, hanem szerver oldalon használhatjuk a kliens oldali program-kódokat. Hagyományosan egy űrlap elküldése esetén a böngészőben történik egy előellenőrzés, hogy az adatok formailag megfelelnek-e a követelménynek, ezzel csökkentve a hálózati forgalmat. Formailag helyes adatok elküldésre kerülnek, ahol szerver oldalon újra ellenőrizni kell őket, mind formailag, mind már adat-logikailag is. Mivel szerver környezetben nem a JavaScript az elterjedt programozási nyelv, a formai ellenőrzést sajnos ott is implementálni kell. Ehelyett a kliens oldali JavaScript-ünket nyugodtan felhasználhatjuk, időt, energiát, költséget, és nem utolsó sorban hiba-lehetőséget megspórolva. &lt;/div&gt;Java kód: &lt;pre class="brush: java"&gt;engine.eval(scriptReader);
if (engine instanceof Invocable) {
Invocable invEngine = (Invocable) engine;
Object result = invEngine.invokeFunction("testStringLength", "valid string", 2);
System.out.println("Result: " + result + "(" + result.getClass().getName() + ")");
result = invEngine.invokeFunction("testStringLength", "invalid string", 20);
System.out.println("Result: " + result + "(" + result.getClass().getName() + ")");
} else System.out.println("NOT Invocable");
&lt;/pre&gt;JavaScript kód: &lt;pre class="brush: javascript"&gt;function testStringLength(input, lenght) {
return input.length &gt;= lenght ? true : false;
}
&lt;/pre&gt;Eredmény: &lt;pre&gt;Result: true(java.lang.Boolean)
Result: false(java.lang.Boolean)
&lt;/pre&gt;&lt;div&gt;Bár a példa nem tükrözi a technológia hasznosságát (így még talán bonyolultabb is), viszont bemutatja, hogyan tudunk JavaScript függvényeket meghívni, és azok visszatérési értékét felhasználni Java kódunkban. &lt;a href="http://jpattern.blogspot.com/2010/02/javascript-ee-json-valaszol.html"&gt;Folyt köv...&lt;/a&gt; &lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Aki szeretne a témáról bővebben is olvasni, az &lt;a href="http://www.ibm.com/developerworks/web/library/wa-aj-javaee/index.html"&gt;itt&lt;/a&gt; talál információt.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-5337232245189927980?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/5337232245189927980/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/02/javascript-ee-izelito-javascript.html#comment-form' title='0 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/5337232245189927980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/5337232245189927980'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/02/javascript-ee-izelito-javascript.html' title='JavaScript EE, első felvonás'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-5215594624894264157</id><published>2010-02-06T13:40:00.016+01:00</published><updated>2010-03-12T14:14:47.287+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='EJB'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='J2EE'/><title type='text'>@Aszinkron EJB &gt;=3.1 hívás</title><content type='html'>Az EJB 3.1 megjelenése számos újítást és egyszerűsítést hozott a rendszerbe, a fejlesztők, mint ahogy a 2.x-ről 3.0-ra váltáskor is, igyekeztek még kényelmesebbé tenni az enterprise alkalmazások készítését. Mivel mi Glassfish alkalmazás-szervert használunk, és a kiadást követő napokban kezdtünk bele egy új projektbe, úgy határoztunk, hogy a friss és ropogós EJB-t használjuk (eddig nem bántuk meg) az implementáláshoz. Az új EJB-t tanulmányozva szembeötlött egy lehetőség, ami mellett nem tudtam szó nélkül elmenni. Úgy néz ki, hogy nem csak nekem jutott eszembe, hogy az alkalmazást, amit fejlesztek párhuzamosítsam, hanem az EJB fejlesztőinek is, ezért megalkották az aszinkron EJB hívás fogalmát. A dolog igen egyszerű, a hagyományokhoz híven @Annotációval kell megjelölni azokat a SessionBean metódusokat, amiket nem a hagyományos módon szeretnénk kiajánlani. &lt;pre class="brush: java;"&gt;package sample;

import javax.ejb.Asynchronous;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;

@LocalBean
@Stateless
public class TestBean {

    @Asynchronous
    public void asyncMethod() {
     //some functionality
    }
}
&lt;/pre&gt;A metódus meghívása a megszokott módon történik, viszont az abban elhelyezett utasítások egy új szálon fognak futni. Ez eddig egyszerű, de mi történik, ha szeretnénk, hogy a hívás valamilyen visszatérési értéket is prezentáljon? Ezt úgy tudjuk megtenni, hogy visszatérési értékként a &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/Future.html"&gt;java.util.concurrent.Future&amp;lt;V&amp;gt;&lt;/a&gt; interfészt jelöljük meg, és annak valamely implementációját adjuk vissza. &lt;pre class="brush: java;"&gt;package sample;

import java.util.concurrent.Future;
import javax.ejb.AsyncResult;
import javax.ejb.Asynchronous;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;

@LocalBean
@Stateless
public class TestBean {

    @Asynchronous
    public Future&amp;lt;String&amp;gt; asyncMethod() {
        try { Thread.sleep(1000);
        } catch (Exception ex) {}
        return new AsyncResult&amp;lt;String&amp;gt;("finish");
    }
}
&lt;/pre&gt;A dolog érdekessége a hívó oldalon történik, hiszen valahogy kezelni kell tudni, hogy az aszinkron hívás éppen milyen állapotban van. &lt;pre class="brush: java;"&gt;Future&amp;lt;String&amp;gt; asyncResponse = testBean.asyncMethod();
//some functionality
while (!asyncResponse.isDone()) {
    try { Thread.sleep(100);
    } catch (Exception ex) {}
}

if (!asyncResponse.isCancelled()) {
    try {
        String response = asyncResponse.get();
    } catch (InterruptedException e) {
    } catch (ExecutionException e) {}
}
&lt;/pre&gt;Láthatjuk mi módon tudjuk "összeszinkronizálni" a hívásokat a szülő szálban. Természetesen meg is lehet szakítani a kérést, a Future cancel metódusával, illetve a rendelkezésünkre állnak a hagyományos Thread notify, notifyAll, és wait metódusai.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-5215594624894264157?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/5215594624894264157/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/02/aszinkron-ejb-31-hivas.html#comment-form' title='0 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/5215594624894264157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/5215594624894264157'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/02/aszinkron-ejb-31-hivas.html' title='@Aszinkron EJB &gt;=3.1 hívás'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7271455030734972271.post-1755067251444211127</id><published>2010-02-05T09:00:00.005+01:00</published><updated>2010-03-12T14:28:18.296+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Trace'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Debug'/><category scheme='http://www.blogger.com/atom/ns#' term='Btrace'/><category scheme='http://www.blogger.com/atom/ns#' term='Bytecode'/><title type='text'>Btrace bemelegítés</title><content type='html'>Egy Sun-os konferencián hallottam először a &lt;a href="http://en.wikipedia.org/wiki/DTrace"&gt;Dtrace&lt;/a&gt; nevű eszközről,  mely egy dinamikus nyom-követő. Sajnos használható formában csak Solaris vagy OpenSolaris platformokra érhető el, és mivel eléggé kernel szintű integrációja van a rendszerrel nehézkes a portolása. Sokáig vártam egy Linux-os verzióra, de sajnos még a mai napig sincs használható állapotban (vagy csak én nem boldogultam vele).&lt;div&gt;A Dtrace működése röviden annyi, hogy van egy saját programozási nyelve a D, és ezen a nyelven kell megírnunk a kernelnek, hogy a vizsgált alkalmazás mely állapot-változásait figyelje. A dolog szépsége, hogy az alkalmzásunkba nem kell "hook" pontokat elhelyezni, tehát a működés megfigyelése teljes mértékben kívülről történik, menet közben megváltoztatható, és nem utolsó sorban, akár éles üzemben is bevethető. A programozó döntése, hogy mit figyel, a megfigyelt eseményekről mekkora mennyiségű információt irat ki a képernyőre, vagy akár egy log fájlba.&lt;/div&gt;&lt;div&gt;Bár a Dtrace platform-függő, létezik egy kifejezetten Java alkalmazásokra szánt Java-s átírat, amit &lt;a href="http://kenai.com/projects/btrace/pages/Home"&gt;Btrace&lt;/a&gt;-nek hívnak (a honlap nem tudom meddig lesz elérhető, mert a project a Kenai forrás-megosztó oldalon van elhelyezve, amit jól megvett az Oracle és jól be is szippantja "belső" használatra). A Btrace egyébként nyílt forrású project, és jelenleg az &lt;a href="http://kenai.com/projects/btrace/downloads/directory/releases%252Fcurrent"&gt;1.1.1&lt;/a&gt;-es verziónál tart. &lt;/div&gt;&lt;div&gt;Használat:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Telepítése igen egyszerű, jóformán nincs, a bináris-t ki kell csomagolni a kívánt helyre.&lt;/li&gt;
&lt;li&gt;Ezután első lépésként meg kell írnunk első Btrace scriptünket, mely annyit csinál, hogy az application.Main osztálynak figyeli a someMethod metódus hívását. &lt;pre class="brush: java;"&gt;package btrace;
import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;
@BTrace public class ClassTracer {
   @OnMethod(clazz = "application.Main", method = "someMethod")
   public static void methodcalled() {
       println("Method called");
   }
}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Következő lépésként a vizsgálandó alkalmazást kell elindítani, egy apró külöbséggel, hogy a JVM-et meg kell kérni, hogy HotSpot módban induljon, ennek módja az alábbi: `java -client -jar sampleapp.jar`. A jar-ba fordított programocska:&lt;pre class="brush: java;"&gt;package application;
public class Main {
   public static void main(String[] args) {
       for (int i = 0; i &amp;lt; 1000; i++) {
           someMethod();
           try { Thread.sleep(100);
           } catch (Exception ex) {}
       }
   }
   public static void someMethod() {}
}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Miután fut az alkalmzásunk egy arra alkalmas eszköz segítségével meg kell tudnunk a futó programunk azonosítóját, PID-jét, Linuxon a top parancs segítségével jutunk sikerre, bár nem ez az egyetlen mód.&lt;/li&gt;
&lt;li&gt;Majd a Btracet kell ráállítani a JVM-re, hogy dolgozza fel a kapott információkat a `btrace PID ClassTracer.java` paranccsal (ha elsőre Connection refused hiba-üzenettel leáll még 1x érdemes megpróbálni futtatni, van, hogy elsőre nem indul el).&lt;/li&gt;
&lt;li&gt;Nincs más dolgunk, mint élvezni a konzolt elárasztó Method called kiírásokat.&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;A módszer legnagyobb előnye, hogy bytecode szinten tudjuk nyomon-követni alkalmazásunkat, azt látjuk, ami a virtuális gépben ténylegesen végrehajtódik.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Még néhány lehetőség (a teljesség igénye nélkül):&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Van mód arra, hogy a clazz vagy method @Annotáció érték helyére reguláris kifejezést illesszünk, így nem csak egy osztályt vagy metódust, tudunk lekezeleni, hanem a mintára illeszkedő összeset.&lt;/li&gt;
&lt;li&gt;Mivel a callback metódusokat mi hozzuk létre, paraméterként számtalan bemeneti változó közül választhatunk, melyeket @Annotációkkal jelölhetünk. Pár példa: @ProbeClassName String probeClass paraméterrel az osztályt, @ProbeMethodName String probeMethod paraméterrel a metódust kapjuk meg. @Return Class returnClass-al megkapjuk a metódus visszatérési értékét (ehhez az @OnMethodban fel kell venni egy location=@Location(Kind.RETURN) értéket), a AnyType[] args-el az átadott argumentumokat.&lt;/li&gt;
&lt;li&gt;Egy Btrace scripten belül párhuzamosan több metódussal is követhetjük az állapotot, ennek köszönhetően a programozó fantáziájára van bízva, hogy pontosan mit is szeretne megfigyelni.&lt;/li&gt;
&lt;li&gt;Lehetőség van időzített ismétlődő műveletek futtatására, így képet kaphatunk a processzor terheltségéről, vagy akár a memória használatról is, ekkor az @OnMethod helyett @OnTimer(long ms) @Annotációt kell használnunk.&lt;/li&gt;
&lt;li&gt;Meglévő Dtrace scriptjeinkkel is összeköthetjük Btrace scriptjeinet, bár ennek nem sok értelmét látom, hisz ahol van Dtrace ott minek használnák a Btracet.&lt;/li&gt;
&lt;li&gt;Létezik egy &lt;a href="https://visualvm.dev.java.net/"&gt;VisualVM&lt;/a&gt; plugin is a Btracehez, így a scripteket a VisualVm-en belül is szerkeszthetjük akár futás-időben is. Mindennapi használatra ezt javaslom egyébként, számtalan ponton könnyíti a munkát.&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;A &lt;a href="http://kenai.com/projects/btrace/pages/UserGuide"&gt;felhasználói kézikönyv&lt;/a&gt; szerintem magáért beszél, továbbá a példa kódok bőbeszédűek, azokból is elsajátítható az eszköz használata.&lt;/div&gt;&lt;div&gt;Még egy fontos jó-tulajdonsága van a Btracenek, mégpedig az, hogy mivel POJO osztályokat kell létrehoznunk, és @Annotációkat használ a sajátságos működéshez, a jar-okat importálva bármely Java képes fejlesztő-eszközhöz van támogatás, ami valljuk be elég nagy könnyebbség a fejlesztőnek.&lt;/div&gt;&lt;div&gt;Zár-szóként remélem kedvet kapott a kedves Olvasó, hogy kipróbálja ezt a remek eszközt, és bár az írás eléggé bevezető jellegű mégis hasznos volt.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7271455030734972271-1755067251444211127?l=jpattern.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jpattern.blogspot.com/feeds/1755067251444211127/comments/default' title='Megjegyzések küldése'/><link rel='replies' type='text/html' href='http://jpattern.blogspot.com/2010/02/btrace-bemelegites.html#comment-form' title='0 megjegyzés'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/1755067251444211127'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7271455030734972271/posts/default/1755067251444211127'/><link rel='alternate' type='text/html' href='http://jpattern.blogspot.com/2010/02/btrace-bemelegites.html' title='Btrace bemelegítés'/><author><name>Richárd Kovács</name><uri>https://profiles.google.com/118246184741611431464</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
