Háttér
Nagy terhelés alatt hajlamosabbak a hibák előjönni, ezért is szoktak összeomlani az előre beharangozott rendszerek az élesítéskor. Emiatt viszont fejlesztés közben is érdemes időnként egy kicsit megküldeni az alkalmazást, hogy lássuk, milyen teljesítményre képes, valamint egyes esetekben kiderülhetnek a szűk keresztmetszetek is.
Webalkalmazásoknál a sebesség csak másodlagos fontosságú. Míg desktopon a 0.2 másodpercnél további választ tekinthetjük lassúnak, a web sokkal lomhább, emiatt a felhasználók is türelmesebbek. Még ha nagyon gyorsan állítjuk is elő az oldalt, a hálózati késleltetés, utána pedig a rendering bőven efölé viszi a tapasztalt válaszidőket. Persze a végtelenségig nem lehet a felhasználót várakoztatni, de néhány másodperc még belefér. Sokkal fontosabb viszont az áteresztőképesség, tehát sok párhuzamos lekérés esetén hányat tud kiszolgálni másodpercenként.
Érdekes kérdés, hogy ebből mennyi is az elég, vagy legalábbis az elvárható, ha egy saját alkalmazást tesztelünk. Persze annyi, amennyi a majdani látogatókat ki tudja szolgálni. 'Educated guess' alapján úgy gondolom, hogy egy jól belőtt, megfelelő cachelést alkalmazó és egy átlagos extranet bonyolultságát felmutató oldalból 50 körül lehet kihozni, de 20-30 még jónak számít. Természetesen ez sokban függ a tesztelendő gép hardverétől is, ezek csak irányszámok.
A tesztelés
A teszteléshez JMeter-t használok, ezzel grafikusan lehet összehúzogatni a teszt lépéseit, valamint az eredményeket is táblázatok és/vagy grafikonok segítségével tudjuk megnézni. A tesztek lelke a Thread Group, itt állíthatjuk be, hogy hány párhuzamos szálon és szálanként hányszor tesztelünk. Ennek a gyerekeként HTTP Request-eket tegyünk le, és állítsuk be a tesztelt URL-eket. Érdemes Assertion-öket adni a kérésekhez, hogy valamilyen szinten a válasz helyességét is meg tudjuk ítélni. Legegyszerűbb a Size Assertion, ezzel már ki tudjuk szűrni, ha mondjuk a terhelésre üres oldalt vagy rövid hibaüzenetet kapnánk vissza. Természetesen nem érdemes a terheléstesztet összetéveszteni az funkcionális teszteléssel, itt feltételezzük, hogy maguk a funkciók működnek.
Érdemes Controller-be venni fel a kéréseket, pl. egy Random Controller-be. Ezzel a benne lévő lekérések közül az egyik fog csak végrehajtódni, de több szál esetén ezzel hatékonyan tudjuk szimulálni azt, hogy párhuzamosan több oldalt is látogatnak. Végül egy Assertion Results-ot és egy Aggregate Report-ot tegyünk le, ezeket figyelve majd láthatjuk az eredményeket, valamint, hogy bukott-e meg az ellenőrzésen valamelyik válasz. Ezután Start, majd idővel megkapjuk az eredményeket. Futás közben érdemes figyelni a rendszer memóriahasználatát, az egyes folyamatok processzorhasználatát, valamint az I/O-t is. Ezzel ki tudjuk szűrni, ha pl. adatbázishoz túl sokszor fordul, vagy ír a lemezre.
A Glassfish minden felhasználóhoz rendel egy session-t, melyekből tesztelés alatt túl sok is létrejöhet, így ronthatja a mért teljesítményt. Ennek elkerülésére maximalizáljuk a számukat, vagy az elévülési idejüket vegyük le.
Ez a tesztelés nem tartalmaz bejelentkezést. Ehhez fel kell vennünk egy Cookie Manager-t, valamint szimulálnunk kell a belépést, valamint a későbbi felhasználói akciókat. A POST-okat kinyerhetjük a böngészőből pl. Firebug-gal, így kényelmesen felépíthetünk bonyolultabb bejárásokat is.
2010. január 25., hétfő
2010. január 23., szombat
Cachelés
Szabvány szerinti portlet cache
A portlet szabvány szerint alapvetően kétféleképpen lehet portletet cachelni. Egyrészt expiration time alapján, másrészt ETag alapon. Mindkettő nagyjából megegyezik a HTTP-ben is alkalmazott módokkal. Egy kicsit az előbbire azért kitérek, mert a kiejtése speciális módon is történhet.
Az alapgondolat az, hogy a portlet egy önálló webalkalmazás, mely kezeli a saját megjelenítését, valamint a hozzá tartozó felhasználói akciókat. Ennek alapján cacheljük el a portletet, és majd csak akkor ejtsük ki, ha történt valami akció (vagy ha lejárt az expiration timer, persze). Probléma abból lehet, hogy ha más portlet is meg tudja változtatni az állapotát egynek, akkor a másik felé történt akcióra is ki kell ejteni. Erre az esemény-alapú IPC nyújt megoldást, mellyel lazán kapcsolhatjuk az összetartozó portleteket.
Fontos még a cachelendő portlet hatálya. Ez lehet publikus vagy privát. Előbbi esetben minden felhasználónak ugyanúgy jelenik meg, így csak egyetlen példányt elég tárolni, utóbbiban azonban minden sessionhöz kell egyet-egyet.
Ez az egyszerű megoldás sok esetben akár elégséges is lehet, így kevés fejfájással sokat tudunk dobni a portál teljesítményén, és ha figyelünk az eseményekre, akkor a cachehibákat is elég jól el tudjuk kerülni. A Liferay egyik sajátossága, hogy jelenleg a vendég felhasználóknak nem gyorsítótáraz. Nem tudom, hogy ez miért jó, de jelenleg így van.
Liferay cachelési megoldása
A Liferay lehetőséget ad a portál cache használatára, melyet magától karbantart, és a Vezérlőpultból lehet is törölgetni, valamint elvileg a fürtözést is támogatja. A használata egyszerű, mint a faék. Vannak cachek, melyekbe betehetünk és kivehetünk objektumokat, kulcs alapján. Az eléréséhez a MultiVMPoolUtil-t tudjuk használni, ennek a getCache(név) metódusával tudunk cache-t elkérni, és a get/put-al az értékeket kezelni. Használhatjuk a SingleVMPoolUtil-t is ugyanígy, a kettő között elvileg a fürtözés a különbség, de ezt még nem próbáltam ki. Az egyes cacheknek a tulajdonságait az ehcache beállító xml-jeiben tudjuk kezelni, itt megadhatunk maximális méretet is a táraknak.
Ez egy elég egyszerű megoldás, amire végülis bármilyen bonyolult egyedi gyorsítótárazást rá tudunk építeni. Mielőtt azonban nekiállnánk valami nagyon általánosnak, érdemes figyelembe venni, hogy JPA szinten az adatbázis entitások már cachelve vannak, ezért egyszerű portleteknél nem biztos, hogy szükség van további gyorsítótárazásra. Először érdemes kimérni, hogy valóban bottleneck-e az adott portlet megjelenítése, és csak utána optimalizálni. Ahogy a mondás tartja: Premature optimalization is the root of all evil.
A portlet szabvány szerint alapvetően kétféleképpen lehet portletet cachelni. Egyrészt expiration time alapján, másrészt ETag alapon. Mindkettő nagyjából megegyezik a HTTP-ben is alkalmazott módokkal. Egy kicsit az előbbire azért kitérek, mert a kiejtése speciális módon is történhet.
Az alapgondolat az, hogy a portlet egy önálló webalkalmazás, mely kezeli a saját megjelenítését, valamint a hozzá tartozó felhasználói akciókat. Ennek alapján cacheljük el a portletet, és majd csak akkor ejtsük ki, ha történt valami akció (vagy ha lejárt az expiration timer, persze). Probléma abból lehet, hogy ha más portlet is meg tudja változtatni az állapotát egynek, akkor a másik felé történt akcióra is ki kell ejteni. Erre az esemény-alapú IPC nyújt megoldást, mellyel lazán kapcsolhatjuk az összetartozó portleteket.
Fontos még a cachelendő portlet hatálya. Ez lehet publikus vagy privát. Előbbi esetben minden felhasználónak ugyanúgy jelenik meg, így csak egyetlen példányt elég tárolni, utóbbiban azonban minden sessionhöz kell egyet-egyet.
Ez az egyszerű megoldás sok esetben akár elégséges is lehet, így kevés fejfájással sokat tudunk dobni a portál teljesítményén, és ha figyelünk az eseményekre, akkor a cachehibákat is elég jól el tudjuk kerülni. A Liferay egyik sajátossága, hogy jelenleg a vendég felhasználóknak nem gyorsítótáraz. Nem tudom, hogy ez miért jó, de jelenleg így van.
Liferay cachelési megoldása
A Liferay lehetőséget ad a portál cache használatára, melyet magától karbantart, és a Vezérlőpultból lehet is törölgetni, valamint elvileg a fürtözést is támogatja. A használata egyszerű, mint a faék. Vannak cachek, melyekbe betehetünk és kivehetünk objektumokat, kulcs alapján. Az eléréséhez a MultiVMPoolUtil-t tudjuk használni, ennek a getCache(név) metódusával tudunk cache-t elkérni, és a get/put-al az értékeket kezelni. Használhatjuk a SingleVMPoolUtil-t is ugyanígy, a kettő között elvileg a fürtözés a különbség, de ezt még nem próbáltam ki. Az egyes cacheknek a tulajdonságait az ehcache beállító xml-jeiben tudjuk kezelni, itt megadhatunk maximális méretet is a táraknak.
Ez egy elég egyszerű megoldás, amire végülis bármilyen bonyolult egyedi gyorsítótárazást rá tudunk építeni. Mielőtt azonban nekiállnánk valami nagyon általánosnak, érdemes figyelembe venni, hogy JPA szinten az adatbázis entitások már cachelve vannak, ezért egyszerű portleteknél nem biztos, hogy szükség van további gyorsítótárazásra. Először érdemes kimérni, hogy valóban bottleneck-e az adott portlet megjelenítése, és csak utána optimalizálni. Ahogy a mondás tartja: Premature optimalization is the root of all evil.
2010. január 22., péntek
Liferay portlet fejlesztése
Már régóta szemezgettem a Liferay portállal, többször is nekiálltam megismerkedni vele, de most jött el az ideje, hogy egy egyszerű, de a legtöbbször szükséges technológiákat használó portletet fejlesszek. Hétfőn kezdtem, ma péntek van, de tegnapra már készenvolt, ez 4 nap. Ennyi idő volt szükséges ahhoz, hogy minden szívást és kezdeti lépést kiismerjek és megoldjak, így a következőt már fél óra alatt is sikerülne elkészíteni.
A portlet egyszerű adatbáziskezelést valósít meg, elneveztem EmotionMeterPortletnek. A funkcionalitási kimerül abban, hogy egy 1-10 közé eső értéket megmutat, és ad 2 gombot, amelyekkel le vagy felfelé piszkálhatjuk, az aktuális értéket pedig adatbázisba teszi.
Telepítések
Szükségünk lesz egy Glassfish-re, Eclipse-re, valamint a Liferay-re, meg egy választott adatbáziskezelőre (esetemben ez MySql, de lehetne bármilyen más relációs is).
Glassfish telepítése: Töltsük le a v3 Web profilt (tölthetjük a teljeset is), telepítsük fel valahova, az alaéprtelmezett beállítások általában jók, én a java elérési utat állítottam csak át.
Eclipse telepítése: Töltsünk egy EE-s Eclipse-t, opcionálisan tegyük fel a Portal Pack-ot (https://eclipse-portalpack.dev.java.net/install.html), valamint a Glassfish adaptert. Ubuntu-n az Eclipse fagyások elkerülésére ki kellett kapcsolni az Assistive Technologiest(+relogin).
Liferay telepítése: Szükségünk lesz a liferay war-os változatára, valamint a dependenciákra. Az utóbbit bontsuk ki a Glassfish lib-jébe, az előbbit meg majd telepítsük az AS-be.
Portlet fejlesztése
Hozzunk létre egy új projectet Eclipse-ben, ha fent van a Portal Pack, akkor a Dynamic Web Projectnél válasszük ki a Portlet 2.0-ás konfigurációt, ekkor létre gof hozni néhány szükséges fájlt magától.
Összesen 8 fájlt kell megírnunk.
WebContent/WEB-INF/portlet.xml: Ide kerülnek a portletet leíró beállítások.
A portlet elkészítése után szeretnénk kitelepíteni. Az Eclipse-s deploy megoldás sajnos nem alkalmazható, mivel az nem futtat le Liferay-specifikus eseményeket, ezért a Liferay autodeploy funkcióját fogjuk használni. Mivel kényelmetlen minden buildelés után másolgatni, ezért érdemes ezt automatizálni. Ebben segít ez a build.xml:
Ezzel készen is vagyunk, van egy egyszerű portletünk, valamint be van lőve egy hatékony fejlesztői környezet is. Sajnos az exploded deploymentet nem tudjuk használni így, azonban a mentéstől számított kitelepítés 4-5 másodperc nálam, ez elfogadható. Bár azt nem próbáltam, de elvileg az állapot is megmarad.
A portlet egyszerű adatbáziskezelést valósít meg, elneveztem EmotionMeterPortletnek. A funkcionalitási kimerül abban, hogy egy 1-10 közé eső értéket megmutat, és ad 2 gombot, amelyekkel le vagy felfelé piszkálhatjuk, az aktuális értéket pedig adatbázisba teszi.
Telepítések
Szükségünk lesz egy Glassfish-re, Eclipse-re, valamint a Liferay-re, meg egy választott adatbáziskezelőre (esetemben ez MySql, de lehetne bármilyen más relációs is).
Glassfish telepítése: Töltsük le a v3 Web profilt (tölthetjük a teljeset is), telepítsük fel valahova, az alaéprtelmezett beállítások általában jók, én a java elérési utat állítottam csak át.
Eclipse telepítése: Töltsünk egy EE-s Eclipse-t, opcionálisan tegyük fel a Portal Pack-ot (https://eclipse-portalpack.dev.java.net/install.html), valamint a Glassfish adaptert. Ubuntu-n az Eclipse fagyások elkerülésére ki kellett kapcsolni az Assistive Technologiest(+relogin).
Liferay telepítése: Szükségünk lesz a liferay war-os változatára, valamint a dependenciákra. Az utóbbit bontsuk ki a Glassfish lib-jébe, az előbbit meg majd telepítsük az AS-be.
Egyebek telepítése: Szükségünk lesz egy XML parserre, ehhez töltsük le a Xerxes-t (http://www.apache.org/dist/xerces/j/), é saf az xml-apis, xerxesImpl, serializer és resolver jarokat tegyük a lib-be. Szükséges még egy JPA provider telepítése is, esetemben ez az EclipseLink lesz. Töltsük le, és az eclipselink és jpa2-es jarokat tegyük a lib-be. Végül az adatbázisunknak megfelelő connector-t is tegyük be a többi jar közé.
Beállítások
Érdemes a fejlesztéshez a Liferay development módot bekapcsolni. Ehhez a domain.xml-be kell egy új jvm opciót felvenni, tehát szúrjuk be az alábbi sort a megfelelő helyre:
-Dexternal-properties=portal-developer.properties
Ezután indítsuk el az AS-t, telepítsük a liferay.war-t, állítsuk be alapértelmezett web modulnak a virtuális szerveren, valamint vegyünk fel egy JDBC Connection Pool-t és Resource-t. Én ezt jdbc/emotion név alá tettem.
Beállítások
Érdemes a fejlesztéshez a Liferay development módot bekapcsolni. Ehhez a domain.xml-be kell egy új jvm opciót felvenni, tehát szúrjuk be az alábbi sort a megfelelő helyre:
Ezután indítsuk el az AS-t, telepítsük a liferay.war-t, állítsuk be alapértelmezett web modulnak a virtuális szerveren, valamint vegyünk fel egy JDBC Connection Pool-t és Resource-t. Én ezt jdbc/emotion név alá tettem.
Portlet fejlesztése
Hozzunk létre egy új projectet Eclipse-ben, ha fent van a Portal Pack, akkor a Dynamic Web Projectnél válasszük ki a Portlet 2.0-ás konfigurációt, ekkor létre gof hozni néhány szükséges fájlt magától.
Összesen 8 fájlt kell megírnunk.
WebContent/WEB-INF/portlet.xml: Ide kerülnek a portletet leíró beállítások.
<?xml version='1.0' encoding='UTF-8' ?>
<portlet-app xmlns='http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:schemaLocation='http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd'
version='2.0'>
<portlet>
<description>EmotionMeterPortlet</description>
<portlet-name>EmotionMeterPortlet</portlet-name>
<display-name>EmotionMeterPortlet</display-name>
<portlet-class>hu.test.BaseRedirectorPortlet</portlet-class>
<init-param>
<name>target-servlet</name>
<value>EmotionServlet</value>
</init-param>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
<portlet-mode>EDIT</portlet-mode>
<portlet-mode>HELP</portlet-mode>
</supports>
<resource-bundle>hu.test.Language</resource-bundle>
<portlet-info>
<title>EmotionMeterPortlet</title>
<short-title>EmotionMeterPortlet</short-title>
</portlet-info>
<security-role-ref>
<role-name>administrator</role-name>
</security-role-ref>
<security-role-ref>
<role-name>guest</role-name>
</security-role-ref>
<security-role-ref>
<role-name>power-user</role-name>
</security-role-ref>
<security-role-ref>
<role-name>user</role-name>
</security-role-ref>
</portlet>
</portlet-app>WebContent/WEB-INF/liferay-portlet.xml: Ide a Liferay egyedi beállításai kerülnek.<?xml version="1.0"?>
<!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 4.2.0//EN" "http://www.liferay.com/dtd/liferay-portlet-app_4_2_0.dtd">
<liferay-portlet-app>
<portlet>
<portlet-name>EmotionMeterPortlet</portlet-name>
<instanceable>true</instanceable>
</portlet>
<role-mapper>
<role-name>administrator</role-name>
<role-link>Administrator</role-link>
</role-mapper>
<role-mapper>
<role-name>guest</role-name>
<role-link>Guest</role-link>
</role-mapper>
<role-mapper>
<role-name>power-user</role-name>
<role-link>Power User</role-link>
</role-mapper>
<role-mapper>
<role-name>user</role-name>
<role-link>User</role-link>
</role-mapper>
</liferay-portlet-app>WebContent/WEB-INF/liferay-display.xml: Ide a Liferay portlet megjelenítését szabályozó beállítások kerülnek:<?xml version="1.0"?>
<!DOCTYPE display PUBLIC "-//Liferay//DTD Display 5.2.0//EN" "http://www.liferay.com/dtd/liferay-display_5_2_0.dtd">
<display>
<category name="category.emotion">
<portlet id="EmotionMeterPortlet" />
</category>
</display>src/META-INF/persistence.xml: Magáért beszél, figyeljünk arra, hogy az src-n belül létre kell hoznunk ezt a könyvtárat.<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="default" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/emotion</jta-data-source>
<properties>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
</properties>
</persistence-unit>
</persistence>src/hu/test/Language.properties: A nyelvi beállítások vannak benne.javax.portlet.title.EmotionMeterPortlet=EmotionMeter Portlet
category.emotion=Emotion
src/hu/test/BaseRedirectorPortlet.java: Ez egy általános portlet, mely egy megadott szervletre irányítja tovább a kérést. Mivel a portlet feldolgozása közben nincsen CDI (nem tudom, hogy miért, de nincsen), szervleteknél viszont van, ezért átirányítjuk a kérést, és utána dolgozzuk fel./** Servlet initparameter: target-servlet */
public class BaseRedirectorPortlet extends GenericPortlet {
public static final String parameterName = "target-servlet";
public static final String modeParameter = "BaseRedirector.mode";
public static enum modes {
VIEW, EDIT, HELP, ACTION
};
public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {
String servletName = getInitParameter(parameterName);
response.setContentType("text/html");
if (request.getAttribute(modeParameter) == null) {
request.setAttribute(modeParameter, modes.VIEW);
}
getPortletContext().getRequestDispatcher("/" + servletName).include(request, response);
}
public void doEdit(RenderRequest request, RenderResponse response) throws PortletException, IOException {
request.setAttribute(modeParameter, modes.EDIT);
doView(request, response);
}
public void doHelp(RenderRequest request, RenderResponse response) throws PortletException, IOException {
request.setAttribute(modeParameter, modes.HELP);
doView(request, response);
}
public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException {
request.setAttribute(modeParameter, modes.ACTION);
String servletName = getInitParameter(parameterName);
getPortletContext().getRequestDispatcher("/" + servletName).include(request, response);
}
}src/hu/test/EmotionServlet.java: Ide kerül a portlet logikája.@WebServlet(urlPatterns = { "/EmotionServlet" })
public class EmotionServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@PersistenceContext(unitName = "default")
EntityManager manager;
@EJB
EmotionEJB ejb;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String actionUrl = ((RenderResponse) request.getAttribute("javax.portlet.response")).createActionURL().toString();
try {
List<Emotion> ems = manager.createQuery(manager.getCriteriaBuilder().createQuery(Emotion.class)).getResultList();
int pos = 5;
if (ems.size() != 0) {
pos = ems.get(0).getPosition();
}
response.getWriter().write("<span><form method=\"POST\" action=\"" + actionUrl + "\"><input type=\"hidden\" name=\"vote\" value=\"minus\"><input type=\"submit\" value=\"-\"></form>TEST:" + pos + "<form method=\"POST\" action=\"" + actionUrl + "\"><input type=\"hidden\" name=\"vote\" value=\"plus\"><input type=\"submit\" value=\"+\"></form></span>");
} catch (Exception e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
boolean up = true;
String param = request.getParameter("vote");
if (param == null) {
return;
}
if (param.compareTo("minus") == 0) {
up = false;
}
ejb.modifyEmotion(up);
}
}src/hu/test/EmotionEJB.java: A CMP-hez szükséges ez az EJB.@LocalBean
@Stateless
public class EmotionEJB {
@PersistenceContext(unitName = "default")
EntityManager manager;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void modifyEmotion(boolean up) {
int lastPos = 5;
for (Emotion e : manager.createQuery(manager.getCriteriaBuilder().createQuery(Emotion.class)).getResultList()) {
lastPos = e.getPosition();
manager.remove(e);
}
Emotion e = new Emotion();
e.setPosition(Math.min(Math.max(lastPos + (up ? 1 : -1), 1), 10));
manager.persist(e);
}
}src/hu/test/entities/Emotion.java: Az entitás.@Entity
public class Emotion implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private int position;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
}Portlet telepítéseA portlet elkészítése után szeretnénk kitelepíteni. Az Eclipse-s deploy megoldás sajnos nem alkalmazható, mivel az nem futtat le Liferay-specifikus eseményeket, ezért a Liferay autodeploy funkcióját fogjuk használni. Mivel kényelmetlen minden buildelés után másolgatni, ezért érdemes ezt automatizálni. Ebben segít ez a build.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="EmotionMeterPortlet" basedir=".">
<property name="warfile" value="EmotionMeterPortlet" />
<property name="deploy.dir" value="/home/sashee/glassfishv3/deploy"/>
<target name="create">
<war destfile="${warfile}.war" webxml="WebContent/WEB-INF/web.xml"
update="true" compress="false">
<classes dir="build\classes" />
<fileset dir="WebContent">
<exclude name="WEB-INF/web.xml" />
</fileset>
</war>
</target>
<target name="copy">
<copy todir="${deploy.dir}" overwrite="true">
<fileset dir=".">
<include name="*.war" />
</fileset>
</copy>
</target>
<target name="deploy">
<antcall target="create" />
<antcall target="copy" />
</target>
</project>Természetesen az elérési utat írjuk át használat előtt. Eclipse-ben beállíthatjuk ezt a project builderei közé, és akkor minden mentés (vagy buildelés) után magától ki is fogja telepíteni a változtatásokat. Érdemes a Liferay-en belül a check interval-t levenni 5s-re, valamint Eclipse-ben az Allocate Console-t kivenni, hogy ne ugorjon el minden mentésnél.Ezzel készen is vagyunk, van egy egyszerű portletünk, valamint be van lőve egy hatékony fejlesztői környezet is. Sajnos az exploded deploymentet nem tudjuk használni így, azonban a mentéstől számított kitelepítés 4-5 másodperc nálam, ez elfogadható. Bár azt nem próbáltam, de elvileg az állapot is megmarad.
Feliratkozás:
Bejegyzések (Atom)