sobota 31. března 2012

Bootstrap.groovy a velké množství počátečních dat

Dnes se mi podařilo vyřešit problém, který jsem ani nedoufal, že se dá vyřešit. Problém spočíval v tom, že v bootstrap souboru v Grailsu zkouším vytvářet velké množství testovacích dat - celý soubor má cca 1000 řádků. Jenže při spuštění Grails hlásil divnou chybu:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000000262b036, pid=4152, tid=8016
#
# JRE version: 6.0_29-b11
# Java VM: Java HotSpot(TM) 64-Bit Server VM (20.4-b02 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# j  BootStrap$_closure1.doCall(Ljava/lang/Object;)Ljava/lang/Object;+87
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
#

---------------  T H R E A D  ---------------

Current thread (0x000000000d985800):  JavaThread "pool-7-thread-1" [_thread_in_Java, id=8016, stack(0x000000000bcf0000,0x000000000bdf0000)]

siginfo: ExceptionCode=0xc0000005, reading address 0x0000000138c797cf

Registers:
RAX=0x000000000bdedd78, RBX=0x000000000bdedd78, RCX=0x00000000d9d1e488, RDX=0x00000000d9d0abff
RSP=0x000000000bdedd88, RBP=0x000000000bdeddb8, RSI=0x000000000d985800, RDI=0x000000000ee83ed0
R8 =0x0000000000000004, R9 =0x00000000d4b4e890, R10=0x000000006dfc4f80, R11=0x000000000ee83f38
R12=0x0000000000000000, R13=0x00000000d9d1d8ce, R14=0x000000000bdee5b0, R15=0x000000000d985800
RIP=0x000000000262b036, EFLAGS=0x0000000000010287

Když jsem ten dlouhej soubor okomentoval zhruba tak 3/4, tak vše jelo. Prvně jsem si myslel, že je to chyba v nedostatku paměti JVM. Zkoušel jsem tedy zvýšit paměť ze 128MB na 512 MB. Chyba však stále byla. Druhé, co mě pak napadlo, byla ne nejnovější verze JVM. Měl jsem 1.6-něco-29 a nejnovější bylo asi 1.6.-něco-31. Updatoval jsem, ale chyba tu furt byla. Už jsem to docela vzdal a smířil jsem se s tím, že prostě budu mít míň testovacích dat, stejně who cares.

Včera jsem však kouknul zpátky na StackOverflow, kam jsem položil otázku na tento problém. Nějakej týpek napsal, ať zkusím rozdělit soubor do více transakcí. Za chvíli jsem našel konstrukci, která to provádí:


Patient.withTransaction {

// vytváření a ukládání doménových tříd…

}


Najednou chyba už nebyla Veselý obličej Jen pak byl problém s tím, že se ukládaly jen 50 entit. Pak jsem zjistil, že se tedy musí dávat .save(flush:true) na konec té transakce, aby se uložila. Také je nutné, aby na sebe odkazující entity byly v jedné transakci. A to je celé Veselý obličej

čtvrtek 29. března 2012

Full Calendar a refresh celého kalendáře

Začal jsem využívat FullCalendar pro vytváření jednoho rezervačního systému. Musím pochválit tento kalendář, je super jednoduchý a nabízí hodně možností customizace. Co mi chvíli trvalo, než jsem našel odpověď, bylo jak ajaxově updatovat kalendář po vytvoření události.
Po chvíli googlení jsem našel nějakou otázku na StackOverflow.com (ale teď ji nemohu najít znova, takže nic). Každopádně bylo tam, jak updatovat kalendář:

$('#calendar').fullCalendar('refetchEvents');
$('#calendar').fullCalendar('rerenderEvents');

$('#calendar') odkazuje na div, kde byl již předtím vytvořen kalendář. Nikam se nemusí ukládat do proměnné odkaz na ten vytvořenej kalendář. Takže super :)

sobota 24. března 2012

Našeptávač pomocí jQuery a Grails

Jak udělat podobnou funkcionalitu našeptávače, jako má Google při vyhledávání, že vám zobrazí ty podobné výrazy? Zrovna včera jsem něco takového udělal.
Šlo o to, že při zadávání část rodného čísla server automaticky vyhledá ty RČ, která obsahují zadávaná čísla a vloží je za uživatele do textboxu. Pro zhotovení jsem použil jQuery UI Autocomplete: http://jqueryui.com/demos/autocomplete/ a serverová část je naprogramovaná v Grails 2.0.1.

Serverová část:
Tato část zahrnuje přijímání parametru z requestu, vyhledávání dle RČ a generování JSON.

První je potřeba udělat vyhledávání pacienta (aplikace je pro zdravotnictví) podle RČ:

List patientList = Patient.createCriteria().list() {
ilike('RC', "%${params.RC}%")
}

Protože je RC uložena jako text, lze vyhledávat pomocí LIKE v SQL.

Tento výsledek pak připravíme pro generování JSON:

def resultList = [] // výsledný list
patientList.each {patient ->
def patientMap = [:] // add to map. jQuery autocomplete expects the JSON object to be with id/label/value.
patientMap.put("id", patient.id)
patientMap.put("label", patient.fullName) // to, co se zobrazí v našeptávači
patientMap.put("value", patient.RC) // to, co se vyplní do vyhledávaného pole
patientMap.put("rc", patient.RC)
patientMap.put("firstName", patient.firstName)
patientMap.put("lastName", patient.lastName)
patientMap.put("insuranceCompany.acronym", patient?.insuranceCompany?.acronym)
resultList.add(patientMap) // add to the arraylist
}
render patientList as JSON // generování JSON v Grails

Jak vidíme, lze zobrazit uživateli něco jiného, než co se vloží pak po vyhledání - zde uživateli zobrazím při zadání části RČ možného pacienta se stejným RČ, ale vložím zase RČ do pole pro RČ.

Klientská část:
Prvně je nutné získat RČ z textboxu - pomocí jQuery:

var rc = $('#patient\\.RC').val(); // za předpokladu, že id textboxu je "patient.RC"

Pak je potřeba vytvořit autocomplete plugin:

$("#patient\\.RC").autocomplete({
delay:500,
minLength:3,
source:contextPath + "/patient/autocompletePatient?rc=" + rc,
select:function (event, ui) { // event handler when user selects a company from the list.
$("#patient\\.id").val(ui.item.id);
$("#patient\\.RC").val(ui.item.rc);
$("#patient\\.firstName").val(ui.item.firstName);
$("#patient\\.lastName").val(ui.item.lastName);
$("#patient\\.insuranceCompany\\.acronym").val(ui.item["insuranceCompany.acronym"]);
}
});

Parametr delay je nastaven na 500 ms, díky tomu server nebude přetěžován dotazy - posílá se až za 500 ms od té doby, kdy klient přestal psát. Minimální délka omezuje vyhledávání až do té chvíle, kdy jsou zadány min. 3 znaky - nevrací se tolik výsledků. Source určuje, kde lze získat zdroj pro zobrazování našeptávač - zde je zdroj vzdálený. Select je event listener, který říká, co se má provést, pokud uživatel klikne na jeden z našeptávaných výrazů - chci, aby se nejen doplnil zbytek RČ, ale také jméno, příjmení a zkratku pojišťovny u daného pacienta.
Pak je nutné, aby se source aktualizoval pokaždé, kdy uživatel něco napíše - lze použít keyup event v jQuery. Celý kód vypadá takto:

$("#patient\\.RC").keyup(function () {
var rc = $('#patient\\.RC').val();
$("#patient\\.RC").autocomplete({
delay:500,
minLength:3,
source:contextPath + "/patient/autocompletePatient?rc=" + rc,
select:function (event, ui) { // event handler when user selects a company from the list.
$("#patient\\.id").val(ui.item.id);
$("#patient\\.RC").val(ui.item.rc);
$("#patient\\.firstName").val(ui.item.firstName);
$("#patient\\.lastName").val(ui.item.lastName); $("#patient\\.insuranceCompany\\.acronym").val(ui.item["insuranceCompany.acronym"]);
}
});
});

Zdroje inspirace:

neděle 18. března 2012

Některé constraint v Grails

Dnes jsem při definování doménové třídy pro novou aplikaci narazil na problém, kdy jsem nevěděl, jaký je rozdíl mezi constrainty size a range. Tušil jsem, že jeden nejspíš bude pro String a druhý pro čísla. Po chvíli prozkoumání zdrojáku v Grails jsem narazil na toto:

třída RangeConstraint metoda processValidate():
....
if (from instanceof Number && propertyValue instanceof Number) {
....

třída SizeConstraint metoda processValidate():
....
if (propertyValue.getClass().isArray()) {...
else if (propertyValue instanceof Collection) {...
else if (propertyValue instanceof String) {...
....
Tedy Size je pro String a Range pro čísla. Také jsem pak narazil na tuto část v dokumentaci: http://grails.org/doc/latest/guide/single.html#gormConstraints.