A következő címkéjű bejegyzések mutatása: kód. Összes bejegyzés megjelenítése
A következő címkéjű bejegyzések mutatása: kód. Összes bejegyzés megjelenítése

2011. október 9., vasárnap

JSTL és a controller az AppEngine-ben

Az eddigi JDO példában a lekért adatokat egy egyszerű szervleten keresztül jelenítettem meg. A jobb használhatóság miatt érdemes szétválasztani a controller és view részeket, így a szervletek mellett megjelennek a JSP lapok, ahol a JSTL-t használom a jobb átláthatóság miatt. Az AppEngine-ben van néhány minimális korlátozás ezért a JSTL-hez is kell egy-két apró trükk. Az első, hogy a JSP lapokon használni kell az isELIgnored="false" kiegészítést.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>

A másik módosítás, hogy az Eclipse projekt build path-jébe fel kell venni a JSTL lib-eket, de ezek nem lehetnek az alap JSTL könyvtárak, hanem a telepített plugin könyvtárban lévőket kell használnunk. Ezeket másoljuk a WEB-INF\lib könyvtárba és adjuk az Eclipse build path-hez a projektben.

\plugins\com.google.appengine.eclipse.sdkbundle_1.5.4.r37v201109211906\appengine-java-sdk-1.5.4\lib\tools\jsp\repackaged-appengine-jakarta-jstl-1.1.2.jar
\plugins\com.google.appengine.eclipse.sdkbundle_1.5.4.r37v201109211906\appengine-java-sdk-1.5.4\lib\tools\jsp\repackaged-appengine-jakarta-standard-1.1.2.jar
http://someprog.blogspot.com/2010/08/google-app-engine-can-not-find-tag.html

Az átalakítás után az alap belépési ponton két irányba irányítjuk át a request-et: home és user.

index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="com.google.appengine.api.users.UserServiceFactory"%>
<%
    if (UserServiceFactory.getUserService().getCurrentUser() == null) {
		response.sendRedirect("/home.jsp");
    } else {
    	response.sendRedirect("/user/vehicles");
    }
%>

A home.jsp oldalról be lehet lépni és tovább lehet menni a felhasználói oldalra, ahogyan eddig is. A user/vehicles átirányítás a járműlistához megy, ha már belépett a felhasználó. A járműlista controllere a web.xml alapján a VehicleServlet, majd feldolgozás után onnan a vehicle.jsp-re ugrik.


VehicleServlet.java
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws IOException, ServletException {

	UserService userService = UserServiceFactory.getUserService();
	User user = userService.getCurrentUser();

	PersistenceManager pm = null;
	try {

		pm = PMF.get().getPersistenceManager();
		AppUser currentUser = UserDao.getUser(pm, user);
		if (currentUser == null) {
			currentUser = initDatabase(pm, user);
		}
		
		request.setAttribute("appUser", currentUser);
		request.getRequestDispatcher("/WEB-INF/jsp/vehicles.jsp").forward(request, response);

	} finally {
		if (pm != null) {
			pm.close();
		}
	}
	
}

A request-be tesszük az appUser-t, átirányítjuk a megjelenítő oldalra, majd a JSP lapon kivesszük a járműveit és azok bejegyzéseit.

vehicles.jsp

A megjelenített adatok halmazának szebb ábrázolása miatt CSS elemeket is használok, így a JSP lapot nem kell telepakolni style elemekel. A létrejött fájlok nagy száma miatt átcsoportosítottam az elemeket, így külön mappába kerülnek a css és a js elemek is. A JSP lapok is kéttéválnak, ezért a gyökérben lesznek a publikus lapok, a WEB-INF\jsp mapában pedig a közvetlenül nem elérhető lapok. Ez utóbbiakat a szervleteken keresztül lehet elérni, így mindig a megfelelő adat is rendelkezésre fog állni.



A frissített kód itt elérhető, a módosítások eredménye pedig ez lett:


2011. október 8., szombat

Kódformázás és kiemelés

A JDO bejegyzés írásakor szembesülnöm kellett azzal, hogy a Blogger nem tartalmaz automatikus kódformázást, így külső alkalmazást kell használni erre. Az http://alexgorbatchev.com/SyntaxHighlighter/ oldalon van is egy jól használható.


 
// SyntaxHighlighter makes your code snippets beautiful without tiring your servers.
// http://alexgorbatchev.com
var setArray = function(elems) {
    this.length = 0;
    push.apply(this, elems);
    return this;
}

2011. október 7., péntek

JDO és az öröklődés

Az AppEngine egyik erőssége a JDO támogatás. A valós kilométer projekt kapcsán 3 alap osztályon keresztül mutatom be hogyan használható a JDO: AppUser, Vehicle és Issue. A bejelentkezett felhasználó adatait az AppUser, az ő járműveit a Vehicle, és a járművekhez tartozó bejegyzéseket az Issue tartalmazza.

Az adatszerkezet kialakításánál kihasználom az öröklődést és az annotációkkal megvalósítható egy-több kapcsolatot. Ennek részletei az AppEngine JDO oldalán és néhány az öröklődést bemutató oldalon részletesen megtalálhatóak.

Források:
Creating, Getting and Deleting Data in JDO
Google App Engine for Java, Part 3: Persistence and relationships
Google App Engine + JAVA + JDO + Inheritance + One-To-Many Relationships
Unindexed Properties

Mivel a további leírásokhoz kódrészletek is tartoznak, így a Google Code oldalán létrehoztam egy fiókot, ahonnan letölthető a folyamatosan bővített forráskód. A kód eléréséhez érdemes telepíteni az Eclipse-hez a Subclipse plugint, így kicsekkolhatjuk és folyamatosan frissíthetjük a kódot.

Az eddigi egyszerű OpenId belépési rendszert bővítettem tovább, így a http://valoskmdev.appspot.com/valoskmdev oldalon, belépés után a felhasználóhoz feltöltött mock adatokat láthatjuk listázva:


Az adatok létrehozása után a helyi gépen létrejön a "\WEB-INF\appengine-generated" könyvtár és benne két fájl. Az egyik az adatokat tartalmazza (local_db.bin), a másik az automatikusan létrehozott indexeket (datastore-indexes-auto.xml).


 \WEB-INF\appengine-generated\datastore-indexes-auto.xml
<!-- Indices written at Thu, 6 Oct 2011 18:57:53 UTC -->

<datastore-indexes>

    <!-- Used 1 time in query history -->

    <datastore-index kind="AppUser" ancestor="false" source="auto">

        <property name="user" direction="asc"/>

        <property name="timeCreated" direction="asc"/>

    </datastore-index>

    <!-- Used 1 time in query history -->

    <datastore-index kind="Vehicle" ancestor="true" source="auto">

        <property name="timeCreated" direction="desc"/>

    </datastore-index>

    <!-- Used 1 time in query history -->

    <datastore-index kind="Issue" ancestor="true" source="auto">

        <property name="issues_INTEGER_IDX" direction="asc"/>

    </datastore-index>

</datastore-indexes>


Az automatikusan létrehozott indexek a fejlesztői környezetben és a fejlesztés kezdetekor jól használhatók, de éles környezetben érdemes saját, manuális beállításokat használni.

A bemutatóban használt kód nem szép, csak a JDO használatát mutatja. A teljes funkciónalításhoz kellenek controller szervletek, megjelenítő jsp lapok is. A statikus DAO-kat is érdemes Spring IoC-vel injektálva helyettesíteni. A bemutatott kód a JDO könnyebb érthetősége miatt ilyen egyszerű.


// JDO
PersistenceManager pm = null;
try {

	pm = PMF.get().getPersistenceManager();
	AppUser currentUser = UserDao.getUser(pm, user);
    	log.info("currentUser in DB: " + currentUser);
    	if (currentUser == null) {
    		currentUser = initDatabase(pm, user);
    	}
    	req.setAttribute("currentUser", "currentUser");
    	out.println("*******************************");
    	out.println("currentUser: " + currentUser);
    	for (Vehicle vehicle : currentUser.getVehicles()) {
    		out.println(" - vehicle: " + vehicle);
    		for (Issue issue : vehicle.getIssues()) {
        		out.println(" + issue: " + issue);
        	}
    	}

} finally {
    	if (pm != null) {
    		pm.close();
    	}
}


A példában látható, hogy elég a felhasználói adatokat betölteni, utána a kapcsolódó collection-öket már egy egyszerű getter-rel beolvashatjuk (lazy loading). A kód további részletei a code.google.com oldalon böngészhetők.