- Szerezzük be a CoolRMI legfrissebb verzióját.
- Indítsuk el az OSGi konténert.
- Installáljuk majd indítsuk el a CoolRMI-t az OSGi konténerben.
Valami ilyesmit kell látnunk:
osgi> ss Framework is launched. id State Bundle 0 ACTIVE org.eclipse.osgi_3.6.0.v20100517 2 ACTIVE com.rizsi.coolrmi_1.1.0
- 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")
- 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.
- 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:
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.");
- Fordítsuk és telepítsük a szolgáltatást.
- Hozzunk létre egy "Java Project"-et, és importáljuk be az imént letöltött jar-t.
- Tegyük elérhetővé a service.SimpleService interfészt, hogy a kliens programunk tudja milyen metódusokat tud meghívni.
- A kliens osztály kódja az alábbi:
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(); } } }
- Futtassuk a klienset, és ellenőrizzük a kimenetet az OSGi konzolon.
osgi> CoolRMI calling.
2010. július 30., péntek
OSGi távoli kapcsolódás CoolRMI segítségével
Az előző bejegyzésben 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 CoolRMI 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.
2010. július 10., szombat
OSGi szárnypróbálgatás
Bár biztosan sokan ismeritek, de legalább hallottatok az Open Services Gateway Initiative-ról, ismertebb nevén az OSGi-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 írást 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.
OSGi pluginek fejlesztésére Eclipse IDE-t választottam, mely amellett, hogy ismeri a MANIFES.MF állomány függőségeit kezelni, rendelkezik beépített OSGi konténerrel (Equinox) 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 jTechnics hasábjain olvashatunk a különféle konténer-implementációkról.
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.
OSGi pluginek fejlesztésére Eclipse IDE-t választottam, mely amellett, hogy ismeri a MANIFES.MF állomány függőségeit kezelni, rendelkezik beépített OSGi konténerrel (Equinox) 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 jTechnics hasábjain olvashatunk a különféle konténer-implementációkról.
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.
A szerviz:
- 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.
- Hozzuk létre a service.SimpleService interfészt:
package service; public interface SimpleService { public void echo(Object message); }
- Majd hozzuk létre service.SimpleServiceImpl néven az szolgáltatás implementációját:
package service; public class SimpleServiceImpl implements SimpleService { @Override public void echo(Object message) { System.out.println(message); } }
- Szerkesszük a service.Activator osztályt a következőre:
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."); } }
- 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.
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"
- A MANIFEST.MF állomány->jobbklikk->Run As->Run Configuration ablakban tudjuk beállítani a konténert a teszteléshez.
- 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.
A kliens:
- Ismét egy hasonlóan paraméterezett "Plug-in Projct"-re lesz szükségünk, de ezúttal caller néven.
- A caller.Activate osztályt hozzuk az alábbi formára:
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 { } }
- 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.
- A META-INF/MANIFEST.MF állomány tartalma legyen a következő:
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"
Fontos, hogy a szervizben exportált service csomagot itt importáljuk. - 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).
A konténer:
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.- Szerezzük be a nekünk megfelelő verziót, jelenleg a 3.6-os a legfrissebb.
- A `java -jar org.eclipse.osgi_3.6.0(.*?).jar -console` parancs futtatásával indíthatjuk el a konténert.
- Installáljuk a bundlekat:
osgi> install file:///path/to/file/service.jar Bundle id is 6 osgi> install file:///path/to/file/caller.jar Bundle id is 7
Eltávolítani az uninstall paranccsal, frissíteni pedig az updatetel lehet. - Az ss paranccsal lekérdezhetjük a telepített bundlek állapotát, mindkettőnek INSTALLED állapotban kell lennie:
osgi> 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
- Utolsó lépésként indítsuk el mindkét bundlet:
osgi> start service Service started. osgi> start caller Service called.
A bundle neve helyett használhatjuk az azonosítóját. - A stop paranccsal állítjuk le a bundlekat:
osgi> stop service Service stopped.
2010. június 24., csütörtök
Barátkozás a Liferay Portlettel, telepítés
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 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.
A Liferay 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 Portletek 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ó.
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 Liferay Eclipse Pluginjé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. Szerk.: A Liferay Eclipse Plugin sajnos csak 6-os verziónál >= 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.
Pozitívumként tapasztaltam, hogy létezik Magyar Liferay közösség, továbbá egy hazai Liferay specialista az I-Logic képében, és az elengedhetetlen Facebook csoport.
A Liferay 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 Portletek 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ó.
- A tanulás/fejlesztés első lépése, hogy letöltjük 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.
- Kicsomagolás közben bőven lesz időnk tanulmányozni a dokumentációt.
- Miután elindítottuk a szervert azonmód elérhető a Lifery Portal rendszer (jó esetben a http://localhost:8080 címen), amire Portlet fejlesztőként egyelőre nem lesz szükségünk.
- A dokumentáció szerint függőségként telepítenünk kell az Apache Antot (Mavennel is működésre lehet bírni, de ezt nem próbáltam), valamint a Jikes fordítót.
- Az első Portletünk létrehozását érdemes a Liferay Plugins SDKn keresztül elvégezni, amely számtalan mintával könnyíti meg a fejlsztést.
- 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.
- Következő lépésként az SDK portlets könyvtárában hozzunk létre egy hello-world projektet.
./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
- A script által létrehozott hello-world-portlet könyvtárba lépve vegyük rá az Antot, hogy készítsen egy buildet.
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
- Utolsó simításként annyi van hátra, hogy Bruno (Admin) nevében jelentkezzünk be az alapértelmezett Portal oldalon (email: bruno@7cogs.com, password: bruno, de szerencsére kattintós módszer is van), és a Welcome -> Add Application menüpont alatt adjuk hozzáadjuk a "Hello World"-öt az oldalhoz (érdemes rákeresni).
Pozitívumként tapasztaltam, hogy létezik Magyar Liferay közösség, továbbá egy hazai Liferay specialista az I-Logic képében, és az elengedhetetlen Facebook csoport.
2010. május 16., vasárnap
Adott pont vonzáskörzetében való keresés MySQL segítségével
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 MySQL adatbázisból ne kerüljön ki olyan érték, amelyet nem kell megjeleníteni.
Lássuk először az 50 km-es körzet problémáját:
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 POINT objektumot. A POINT beépített MySQL objektum, így értelem-szerűen a mező típusának is az van deklarálva.
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 POINT objektumot. A POINT beépített MySQL objektum, így értelem-szerűen a mező típusának is az van deklarálva.
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 ;
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 POLYGONE 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.
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)";
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).
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.
DELIMITER // CREATE TRIGGER SetDistrict BEFORE INSERT ON `horeca` FOR EACH ROW BEGIN IF NEW.`postal_code` < 2000 THEN SET NEW.`district` = SUBSTR(NEW.`postal_code`, 2, 2); END IF; END; DELIMITER ;
Ezek után létrehoztam egy táblát, amely a szomszédos kerületek mátrixát tartalmazza.
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');
Az alábbi lekérdezéssel pedig könnyedén kinyerhető adott kerület és a vele szomszédosak az adatbázisból.
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)";
2010. április 28., szerda
Weboldal beüzemelése Linux, Apache, Glassfish alapon
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.
A beállítás lépései:
- Glassfish domain létrehozása.
# asadmin create-domain --adminport 4848 --savemasterpassword domain1
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.
- Indító script megírása, és a megfelelő runlevel-be helyezése.
#!/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
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.
- Apache web-szerver beállítása
NameVirtualHost *:80 <VirtualHost *:80> 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 </VirtualHost>
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.
Feliratkozás:
Bejegyzések (Atom)