Stronnice Chlebika – Java Blog for Newbies

Czerwiec 13, 2009

Java exPress, a sprawa Chlebika

Zaszufladkowany do: Blog, Grails, Life — chlebik @ 12:25 am

Jest takie pismo.STOP.Wydawane od paru miesięcy, ale niezbyt często.STOP.Nazywa się Java exPress.STOP.Ostatnio wyszedł nowy numer.STOP.W numerze artykuł Mroza o Grails.STOP.Link do mojego bloga w polecanych.STOP.Wzruszyłem się.STOP.

Czerwiec 8, 2009

HowToJava, ostateczny szlif

Zaszufladkowany do: Grails, Groovy, HowToJava — chlebik @ 10:58 pm

Po dłuższym okresie walki z językiem Groovy i Grailsami uznałem, że przyszedł czas by zakończyć prace nad aplikacją HowToJava. Ostateczny kształt nie poraża może możliwościami, ale podczas prac dowiedziałem się tylu nowych rzeczy, że to co powstało w pełni mi wystarcza (pewnie nie raz sypnie wyjątkiem na serwerze :).

Rzecz głównie w zupełnie odmiennej filozofii pracy z kodem (w odróżnieniu choby od używanego na co dzień PHP). Domyślam się, że to sprawka samego języka, ale też i frameworka, który upraszcza wszystko niemożebnie, co momentami jednakże prowadzi do wkurzających wręcz sytuacji, z którymi musiałem sobie poradzić. Z perspektywy czasu nie uważam poznania tej dziedziny programowania w Javie za stracony! Jak radził na początku Jacek Laskowski poznanie Grailsów daje możliwość zapoznania się z świetnie działającym konglomeratem kilkunastu uznanych powszechnie technologii/frameworków/abstrakcji, a ostatecznie przy bliższym poznaniu składowych – docenienie Grailsów w pełni.

Zakładam, że jednakże minie jeszcze trochę czasu, zanim będę mógł kompetentnie się w tej dziedzinie wypowiadać. Na razie kontynuuję przygotowania do certyfikacji SCJP. W końcu czerwca udaję się na tygodniowy urlop, zakładam zatem, że do tego czasu z całą pewnością do egzaminu nie będę podchodził. Ale lipiec to jak najbardziej realny termin. Z braku wykształcenia kierunkowego muszę nadrabiać zdawaniem certyfikatów, cóż począć :)

Zastanawiam się także nad rozpoczęciem prac nad nowym projektem (no bo ile można katować czystą Javę :) – teraz tylko pytanie o technologię. Czy wyjść z poziomu niewiele niższego niż Grails? Może zatem czysty Hibernate i Spring? A może trochę nowoczesności – GWT lub Seam (ten ostatni ma fajną dokumentację)? Ciągną też rozwiązania niskopoziomiowe – JSP, JSF, Struts. Każdy wybór ma swoje wady i zalety. Powiedzmy sobie szczerze – nikt raczej nie zatrudnia młodzika na juniora by od razu po przyjściu kodował olbrzymie systemy w Springu. Zaś do konserwacji przedpotopowego kodu pisanego w JSP już jak najbardziej tak. Ankieta u Jacka również jest ciekawa i daje wiele do myślenia. No nic, dam sobie kilka dni do przemyśleń.

PS. HowToJava można też obejrzeć pod łatwiejszym adresem http://chlebik.pl

Maj 22, 2009

Grailsujemy dalej – zadajemy pytania w HowToJava

Zaszufladkowany do: Grails, HowToJava — Tagi: , , — chlebik @ 10:23 pm

Po dłuższej przerwie wróciłem do kodowania w Grails. Zostało jeszcze parę rzeczy, by uznać, że na bardzo podstawowym poziomie, przykładowa aplikacja HowToJava jest skończona. Dziś pierwszy krok na tej drodze – możliwość tworzenia tematów/zadawania pytań.

Warstwa widoku, walidacji błędów, klas domenowych nie wychodzi poza rzeczy, której do tej pory opisywałem. Na tym temacie zatem nie będę się specjalnie skupiał. Jedyną rzeczą, którą możnaby przećwiczyć jest kolejny plugin, który zamierzam użyć – FCKEditor. Biblioteka ta jest uniwersalnym GUI przeznaczonym dla stron internetowych. Umożliwia wyczynianie cudów z wpisywanym tekstem – dla potrzeb mojej aplikacji jest tego aż nadto. Zainteresowanych samym edytorem mogę odesłać na jego stronę domową. Zaś dla nas bardziej interesująca będzie strona opisująca plugin, który umożliwia wykorzystanie mocy tegoż edytora w Grails.

Standardowo przechodzimy do katalogu z aplikacją i wydajemy polecenie:

grails install-plugin fckeditor

I po chwili możemy cieszyć się kolejną funkcjonalnością w aplikacji. Zainstalowałem wersję 0.9.2, która jest najnowszą, mam zatem nadzieję, że da się z niej korzystać. Kiedy integrowałem swego czasu FCKEditora z Zend Framework miałem trochę zabawy. No i wykrakałem…

Okazuje się, że skrypt ściągający plugin nie rozpakowywuje go odpowiednio. Zatem musiałem wybrać się na stronę pluginu i ściągnąć go dla pewności ręcznie. Następnie plik *.zip trzeba rozpakować do katalogu /plugins, a konkretniej do folderu fckeditor-0.9.2. Zaś w widoku wrzucamy taki oto kod:

<fckeditor:editor
name="question"
width="100%"
height="200"
toolbar="Basic"
fileBrowser="default">
</fckeditor:editor>

No i działa. Wrzuciłem minimalny pasek z narzędziami, ale to można łatwo zmienić. Dla bardziej zaawansowanej konfiguracji polecam przyjrzenie się plikowi fckconfig.js w katalogu z pluginem.
Walidacja jest prosta jak konstrukcja gwoździa (ogranicza się tylko i wyłącznie do walidacji klasy domenowej). Jedynym problemem była data dodania. Jakoś na początku swej przygody z programowaniem dla web pałałem prawdziwą miłością do typu DATETIME w MySQL. Jednakże jakiś czas temu zostałem wyleczony z używania tej kolumny, na rzecz TIMESTAMPA (unixowego). Po prostu wyszukiwanie po kolumnach jest kilkakrotnie szybsze (wartości TIMESTAMP są zapisywane jako INT(11) ), a także sam format TIMESTAMP jest o wiele bardziej uniwersalny – każdy język programowania (no, przynajmniej te w których kodowałem) miał bardzo sympatyczne wsparcie dla tego formatu. W Javie obecny TIMESTAMP (uwaga: konkretnie chodzi mi o ilość sekund od 1970, a nie milisekund, co zresztą w kodzie widać) można wydobyć w taki sposób:

Math.round( System.currentTimeMillis()/1000 )

I takim oto szybkim obejściem dało się to załatwić. Tak wygląda widok dodający nowy temat:

dodawanieTematu

Maj 18, 2009

Chlebik walks alone – deployment HowToJava

Zaszufladkowany do: Blog, Grails, HowToJava — Tagi: , , — chlebik @ 11:49 pm

Zgodnie z zapowiedziami dorobiłem się w końcu własnej przestrzeni w necie. Do tego przestrzeni całkiem sympatycznej bo z własnym Jetty i paroma innymi rzeczami. Rzecz jasna trzeba było spróbować ponownie pokazać światu moje Grailsowe wypociny.

Samego procesu deploymentu do tej pory nie ruszałem. Lokalnie uruchamianie aplikacji odbywa się poprzez NetBeans, zapędów do konfiguracji serwerów/kontenerów też póki co nie mam. Jak wspomniałem również wcześniej – niedługo Helion wyda na podobne tematy książkę, zatem poczekam jeszcze trochę by zapoznać się z tematem dogłębniej. Jednakże by wrzucić coś na serwer docelowy z całą pewnością wypada skonfigurować dostęp do bazy danych. Oczywiście by to osiągnąć wypada wpisać odpowiednie dane dostępowe do DataSource.groovy.

Kiedy się z tym uporałem przyszedł czas na zbudowanie paczki dystrybucyjnej. W Grails jest to proste jak konstrukcja gwoździa. Wystarczy przejść do katalogu z aplikacją i wydać polecenie:

grails war

Pomieli, przemieli i ostatecznie w tymże katalogu wypluje nam paczkę (plik z rozszerzeniem *.WAR). Teraz ten plik (ja jego nazwę zmieniłem na howtojava.war, wyrzucając numerację wersji) należy wrzucić na serwer produkcyjny. Domyślnie Jetty wszystkie aplikacje próbuje odnaleźć w katalogu /webapps. Konwencja ta jest słuszna, zatem w tymże katalogu stworzyłem następny (o nazwie a jakże howtojava) i do niego wrzuciłem plik WAR. Teraz przyszedł czas na konfig serwera.

Należy zajrzeć do pliku jetty.xml, który rezyduje w podkatalogu /etc podstawowego folderu serwera. Możemy z góry określić wspólną konfigurację dla serwera (jak np. uruchamia się na nim jedną aplikację) – i to są ustawienia domyślne. Ja jednakże postanowiłem, iż dla każdej aplikacji (na razie jedna, ale może będzie i więcej) będę posiadał oddzielny plik konfiguracyjny. Wyedytowałem plik i dodałem takie oto zapisy:

<Call name="addLifeCycle">
<Arg>
<New class="org.mortbay.jetty.deployer.ContextDeployer">
<Set name="contexts"><Ref id="Contexts"/></Set>
<Set name="configurationDir"><SystemProperty name="jetty.home" default="."/>/contexts</Set>
<Set name="scanInterval">5</Set>
</New>
</Arg>
</Call>

W folderze z Jetty należy utworzyć wskazany katalog (/contexts). I następnie wrzucamy do niego pliczek o wdzięcznej nazwie Howtojava.xml. Wygląda on tak:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
<configure class="org.mortbay.jetty.webapp.WebAppContext">
<Set name="contextPath">/howtojava</Set>
<Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/howtojava/howtojava.war</Set>
</configure>

Jednakże przy takiej konfiguracji, wejście na podstawową domenę powoduje brzydkie zgłoszenie błędu 404 i wylistowanie wszystkich dostępnych kontekstów. Dlatego tez dodałem jeszcze jeden plik w katalogu /contexts – default.xml. Oto on:


<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
<configure class="org.mortbay.jetty.webapp.WebAppContext">
<Set name="contextPath">/</Set>
<Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/howtojava/howtojava.war</Set>
</configure>

Dzięki temu domyślnie wskazanie na domenę leci pod konkretny adres. Uruchomienie serwera to odpalenie pliku start.jar z odpowiednimi uprawnieniami. Mój jakże miły admin (choć nie wiem czy to on, czy po prostu lekko wyedytował defaultowe skrypty) dostarczył mi 2 pliczki, które opakowywują uruchamianie i zamykanie serwera: Start.sh:

#!/bin/sh

export port=$(expr $(cat base_port) + 0)

echo "Starting the Jetty process at port $port"

nohup java -Xmx$(cat ram_size)m -Drun.mode=production -Djetty.port=$port -jar -server start.jar etc/jetty.xml >> logs/std.out 2>> logs/err.out &
echo $! > prod.pid
echo "The PID is $!"

Nie jest to nic skomplikowanego. Obok tego pliku w katalogu leżą 2 inne pliki – jeden (base_port) zawiera informację o porcie, na którym ma być uruchomiony serwer, a drugi (ram_size) to wejściowa ilość RAMu dla serwera. Przypisany ID procesu jest zapisywany do pliku, a wyjście zarówno System.out, jak i strumień błędów są przekierowywane do odpowiednich katalogów. Drugi skrypt to po prostu odczytanie zapisanego ID procesu z pliku i zabicie tego procesu. Uwadze polecam również plik /bin/jetty-service.conf, gdzie można znaleźć kilka ciekawych zmiennych, którymi można się pobawić.

I tyle. Teraz wystarczy odwiedzić : pewien adres by zobaczyć jak śmiga HowToJava. Subdomena póki co mi nie chwyciła, ale jak załapie to z całą pewnością przepnę ją na nowy adres.

Gdyby ktoś był zainteresowany hostingiem dla aplikacji Grails i bardzo profesjonalną obsługą, wówczas wystarczy napisać mejla na adres admin@serenity.org.pl i ładnie poprosić o ofertę. Jako, że serwery cały czas są w fazie ‘beta’ (w sumie GMail też jest :), zatem szereg rzeczy jeszcze nie działa tak jak powinno, a części póki co nie ma. Ale ceny bardzo konkurencyjne, zaś z jakości usługi jestem póki co bardzo zadowolony.

Maj 15, 2009

Nic nie robi, a się robi

Zaszufladkowany do: Blog, Grails, Life, SCJP — Tagi: , , , — chlebik @ 9:37 am

A taki śmieszny tytuł mi dziś do głowy przyszedł. A wziął się stąd, że im mniej piszę, tym większy ruch na blogu. Ciekawe i polecam do przemyśleń filozoficznych.

No ale do rzeczy. Zaniedbałem się ostatnio w pisaniu przyznaję. Jednakże to kwestia tylko braku czasu, a nie chęci. Pomagałem kumplowi w projekcie inżynierskim, nadrabiałem zaległości w lekturze czego innego niż dokumentacje i specyfikacje, a także w końcu się wyspałem. Teraz jednakże czas powrócić – skończyłem właśnie lekturę drugiego rozdziału z podręcznika SCJP, moje przemyślenia i wnioski już wkrótce.

Druga kwestia to start mojego wykupionego hostingu. Na razie dostałem na pewno prywatne Jetty, serwer WWW, baze, poinstalowane Grailsy no i zobaczymy co z tego wyniknie. Relacja z wdrażania aplikacji na serwer inny niż lokalny również niedługo. Will Frederico marry Francesca? Stay tuned.*

* Takie śmieszne wtrącenia są częstym elementem w podręczniku do SCJP. Naprawdę czasami wymiatają :)

Kwiecień 28, 2009

TDD na przykładzie Grails – klasy domenowe i kontrolery

Zaszufladkowany do: Grails, Groovy, HowToJava — Tagi: , , , , — chlebik @ 3:57 pm

Zaczynam się sypać. Poważnie. Znów mnie coś w kręgosłupie strzyka, nic fajnego, siedzenie jeszcze jakoś mi wychodzi, ale chodzenie to już o wiele gorzej. Choć jedyna rzecz pozytywna z tego wynika – porządki na dysku i kolejny wpis na blogu.

W poprzednim wpisie przedstawiłem specyfikę TDD na przykładzie ograniczeń dla klas domenowych. Przed rozpoczęciem dalszej zabawy przytoczę dodatkowe asercje, które oferuje nam klasa GroovyTestCase, która jest używana zarówno do testów jednostkowych jak i integracyjnych w Grails (oczywiście skopiowane z dokumentacji).

  • assertArrayEquals(Object[] expected, Object[] value)
  • assertLength(int length, char[] array)
  • assertLength(int length, int[] array)
  • assertLength(int length, Object[] array)
  • assertContains(char expected, char[] array)
  • assertContains(int expected, int[] array)
  • assertToString(Object value, String expected)
  • assertInspect(Object value, String expected)
  • assertScript(final String script) // assert that a script runs without exceptions
  • shouldFail(Closure code) // assert that an exception was thrown in that closure
  • shouldFail(Class clazz, Closure code) // the same but for a class

Trochę ułatwiają życie, bez konieczności pisania własnych asercji na szybko. Przetestowaliśmy już ograniczenia dla kasy domenowej, teraz zajmijmy się jej metodami. Założenie, że wypadałoby testować takie metody jak save() czy metody wyszukujące jest trochę kontrowersyjne. Z jednej strony dobrze byłoby testować także i je, ale z drugiej strony to tak, jakbyśmy wątpili w umiejętności programistyczne twórców frameworka. Dlatego też nie jest to zalecana praktyka. Skupimy się zatem na przetestowaniu dopisanej przez programistę metody – a najprostszą metodą tego typu będzie oczywiście toString().

Nasz HtjTopic będzie oczywiście służył do ładnego wylistowania jego zawartości na stronie w formie tabeli, czy ładnej listy, ale zaprezentowana metoda toString() ma tylko charakter podglądowy (dodałem własności pominięte w poprzednim wpisie).

String toString() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd")
"${question} zadane: " + formatter.format(adddate) + " przez ${author.nick}"
}

Test napisany dla tej metody wygląda tak:

void testToString() {
DateFormat df = DateFormat.getDateInstance()
String adddateString = "2009-04-28"
Date adddate = df.parse(adddateString)
def topic = new HtjTopic( question: "Czy potrzebuję komputera?", author: new Htj_Users( nick: "Chlebik" ), adddate: adddate )
assertEquals "Czy potrzebuję komputera? zadane: 2009-04-28 przez Chlebik", topic.toString()
}

I tyle. Zasadniczo wywołanie metod napisanych przez nas samych w klasie domenowej jest podręcznikowym wręcz przykładem testu jednostkowego. Nie trzeba nawet odwoływać się do innych metod, dziedziczenia, etc. Sprawa jest zatem dość prosta i nie trzeba więcej komentarza.

Zajmijmy się teraz kontrolerami. Tutaj sprawa jest o wiele trudniejsza, gdyż kontroler (no chyba, że służy tylko jako ‘zaplecze’ dla wyświetlenia widoku) może zwracać często różne wyniki w zależności od przekazanych parametrów, rodzaju odebranego żądania i tak dalej. To kontrolery z definicji są najbardziej pokomplikowanymi elementami aplikacji zgodnej z wzorcem MVC. Mimo to da się je oczywiście testować, co zresztą zaraz zrobimy.

Testowanie kontrolerów to już nie są testy jednostkowe, choć typologia w tej dziedzinie jest momentami kulawa. Grailsy domyślnie przy tworzeniu klas domenowych tworzą klasę w katalogu z testami jednostkowymi, zaś przy tworzeniu kontrolerów odpowiedni test zostaje stworzony w ktalogu z tzw. testami integracyjnymi. Jak można się domyślić, testy integracyjne służą przetestowaniu relacji i przepływu w danym artefakcie (tutaj akurat kontrolerze). Nie odchodząc za bardzo znów do tworzenia kodu do testów, spróbujmy odnieść się do kodu klasy UsersController, który powstał przy okazji pisania kodu autoryzującego.

Grailsy (jak podaje dokumentacja) domyślnie wrappują tego typu testy (integracyjne) w obiekty Spring Mock Library, co pozwala na odwoływanie się do fałszywych obiektów żądania czy odpowiedzi (sesji też). Dzięki temu możemy stworzyć dla każdego testu warunki identyczne z tymi, w których najczęściej dana akcja będzie pracowała (i z tymi skrajnymi też, testowanie warunków brzegowych to istotna część procesu testowania). By nie przedłużać i komplikować napiszemy szybki test dla akcji login. Sprawdza ona czy użytkownik jest zalogowany, jeśli nie jest to wyświetla formularz, albo dobiera się do tablicy POST. Jednakże kiedy użytkownik nie jest zalogowany (session.user jest puste), wówczas redirectuje na stronę główną oraz wyświetla komunikat o tym, iż użytkownik jest już zalogowany. Oto jak wygląda test:

void testLogin() {
def c = new UsersController()
c.session.user = "jakisobiekt"
c.login()
assertEquals "/", c.response.redirectedUrl
}

I oczywiście test przechodzi nasz kod poprawnie. Należy zwrócić uwagę na ciekawą metodę redirectedUrl, która umozliwia zbadanie docelowego adresu, pod który zostanie skierowane nasze żądanie. Więcej tego typu metod można znaleźć w dokumentacji Spring Mock.

Oczywiście takie same założenia co do testowania odnoszą się do usług, czy command objectów. Testować można również własne znaczniki GSP. Zasadniczo testować można wszystko do czego zresztą gorąco zachęcam.

TDD na przykładzie Grails – klasy domenowe i ich reguły

Zaszufladkowany do: Grails, Groovy, HowToJava — Tagi: , , , — chlebik @ 12:22 am

Nie tak dawno temu zawędrowałem na spotkanie WJUGa, na którym poruszono temat DDD (Domain Driven Development). Okazało się, że Grailsy są pięknym przykładem tego podejścia w praktyce. Jednakże na razie skupimy się na tym, co do tej pory omijałem w moich postach dotyczących frameworka – testach. Pisałem o nich wcześniej w kontekście JUnit, teraz zobaczmy jak zostało to zrobione w Grails.

Framework rozróżnia dwa typy testów – testy jednostkowe oraz testy integracyjne. Póki co zajmiemy się tymi pierwszymi. Zacznijmy od klas domenowych, gdyż to na ich przykładzie najlepiej pokazać jak powinien przebiegać proces budowania aplikacji w oparciu o testy. Jako podstawa posłuży nam kolejna funkcjonalność w aplikacji HowToJava – zadawanie pytań. Stworzymy zatem dwie klasy dziedzinowe – jedna będzie reprezentowała założony temat (wątek pytania, tak samo działający jak post na forum), a także konkretny wpis w ramach tego pytania/tematu.

Tworzymy zatem klasę dziedzinową HtjTopic. Wykonanie polecenia:

grails create domain-class

Powoduje utworzenie nie tylko klasy domenowej, ale także stosownych dla niej testów. Utworzona klasa testowa wygląda tak:

import grails.test.*

class HtjTopicTests extends GrailsUnitTestCase {
protected void setUp() {
super.setUp()
}

protected void tearDown() {
super.tearDown()
}

void testSomething() {

}
}

Mamy jak widać metodę wykonywaną na rozpoczęciu testu, a także jedną wywoływaną na koniec. Oczywiście w środku mamy też metodę będącą sednem testu. Przed jego wykonaniem zastanówmy się jednakże czego potrzebujemy w naszej klasie domenowej. Z całą pewnością takie pytanie zadane w serwisie musi mieć autora, tytuł z całą pewnością również by się przydał, data dodania myślę też by się przydała. Ostatecznie po szybkim procesie myślowym powstała taka oto lista:

  • tytuł będący łańcuchem o określonej długości
  • data utworzenia wpisu
  • autor (będzie to zalogowany użytkownik)

Dodamy też później kategorię, do której przynależy nasze pytanie, jednakże by nie komplikować kodu póki co, pozostaniemy przy naszej wersji. Generalna zasada TDD głosi, aby nie pisać nowego kodu, jeżeli testy nie kończą się sukcesem. Na razie nie mamy ani testów, ani kodu, zatem problem jest czysto akademicki. Zasadniczo jedyną rzeczą, którą można przetestować w wyraźny sposób póki co są ograniczenia i to, czy nasza klasa ich przestrzega. Stworzymy sobie obiekt imitujący zachowanie prawdziwego obiektu (tzw. mock) i na nim będziemy testować. Można by zastanowić się po co testować ograniczenia wpisywane do klasy, które mają na celu właśnie kontrolę i testowanie, czy poprawne wartości zostały wpisane? Autorzy frameworka odpowiadają na to pytanie prosto – bo tak :), ale też by uniknąć literówek, które (rzekomo) są plagą w tym przypadku. Grailsy z definicji ułatwiają życie i w związku z czym oferują gotowy szkielet do testowania. Nasza metoda testująca powinna wyglądać na razie tak:

void testConstraints() {
def existingTopic = new HtjTopic()
mockForConstraintsTests(HtjTopic, [ existingTopic ])

// Testujemy constraint notNull
def topic = new HtjTopic()
assertFalse topic.validate()
assertEquals "nullable", topic.errors["question"]

// Testujemy constraint blank
topic = new HtjTopic( question: "" )
assertFalse topic.validate()
assertEquals "blank", topic.errors["question"]

// Testujemy constraint minSize
topic = new HtjTopic( question: "test")
assertFalse topic.validate()
assertEquals "size", topic.errors["question"]

// Testujemy constraint maxSize
topic = new HtjTopic( question: "test test test")
assertFalse topic.validate()
assertEquals "size", topic.errors["question"]

// Na koniec dla pewnosci, ze poprawne dane powoduja przejscie testow
topic = new HtjTopic( question: "Jak pisać" )
assertTrue topic.validate()
}

Jej zrozumienie nie powinno nastręczać problemów. Dziwić mogą tylko dwie pierwsze linijki. Wywołanie metody mockForConstraintsTests w takim kształcie ‘uzbraja’ egzemplarze klasy domenowej w metodę validate(), co pozwala na sprawdzenie poprawności constraints (Uwaga: skróciłem długość tematu by ładniej to wyglądało w kodzie na blogu. Normalnie oczywiście powinien być dłuższy). Testy (uruchamiane w NetBeans kombinacją klawiszy Alt-F6) pokażą po pierwszym wywołaniu błędy bo i pewnie nie ma jeszcze klasy, której to obiekty chcemy utworzyć. Oto kod klasy HtjTopic:

class HtjTopic {

String question

static constraints = {
question( blank: false, size: 5..10, nullable: false )
}
}

To oczywiście na razie tyle, aby łatwiej było pokazać ideę testów. Wyniki testów możemy ładnie sobie obejrzeć w przeglądarce (katalog /tests w folderze projektu) – każdy błąd lub niepowodzenie testu zostanie odnotowane. Filozofia pisania testów jest taka, aby nie napychać jednej metody chamską wręcz ilością linii kodu. Należy to rozbić, np. ograniczając metody do konkretych pól, albo też do walidacji metodami poszczególnych ograniczneń (np. tylko wartości NULL).

Dodam jeszcze tylko, że testowanie odbywa się w spejcalnym środowisku testowym, które posiada oddzielny wpis konfiguracyjny w pliku DataSource.groovy. Oto jak powinna wyglądać konfiguracja, aby opierała się na HSQLu.

test {
dataSource {
pooled = true
driverClassName = "org.hsqldb.jdbcDriver"
username = "sa"
password = ""
dbCreate = "create-drop"
url = "jdbc:hsqldb:mem:testDb"
}
}

To tyle na dziś. W następnym wpisie zajmiemy się dodatkowymi metodami klas dziedzinowych oraz zerkniemy na inne artefakty, które można podpiąć pod testy.

Kwiecień 21, 2009

Chlebik walks alone

Zaszufladkowany do: Blog, Grails, HowToJava, Life — Tagi: , , — chlebik @ 8:56 am

Ah taki przewrotny tytuł tegoż wpisu :) Konkretnie rzecz w tym, iż z czasem usługa na mor.ph przestała mi wystarczać. Jednak co własna przestrzeń to własna przestrzeń. Jednakże jakoś do tej pory nie za bardzo miałem okazję, aby zapoznać się z kwestią administracyjną Javy. Dlatego zrobiłem to co każdy mądry człowiek by uczynił – zatrudniłem do tego ludzi.

A konkretnie wstępnie ukończyłem rozmowy w sprawie przestrzeni serwerowej.  Na razie nie będzie to nic wielkiego, ale umożliwi pokazanie wszystkich moich “dzieł” :) szerokiej publiczności bez oglądania się na oferowane rozwiązania. Płacę i mam pewną usługę. Wstępnie na serwerze będzie też działał serwer WWW z PHP zatem może uda się postawić jakąś małą platformę WWW dla Grailsów, gdyż póki co w polskim internecie posucha jeśli chodzi o strony o tej technologii.

Póki co zatem HowToJava znika z netu, wróci niebawem. Dział również czynię niewidocznym, mam nadzieję, że powróci za mniej więcej dwa tygodnie wraz z szeregiem innych rzeczy.

Kwiecień 17, 2009

Uzyszkodnicy i aplikacja, cz. II

Zaszufladkowany do: Grails, HowToJava — Tagi: , , , — chlebik @ 10:52 pm

Użytkowników w bazie powiedzmy, że już mam. Teraz wypadałoby zrobić coś, aby dać im możliwość zarejestrowania się w serwisie.

Proces ten jest prosty jak konstrukcja gwoździa. Wpisujemy login, wpisujemy hasło (dwa razy), wpisujemy napisik z CAPTCHA i po walidacji formularza ( np. nick unikalny i hasło nie za krótkie ) tworzone jest konto w serwisie. Do tego można się na nie nawet zalogować! Zasadniczo skoro domenę już mamy (przynajmniej na początek), widok sobie zaraz stworzymy, kontroler też ogarniemy. Jedyną rzeczą, o której póki co nie za bardzo mam pojęcie to wdrożenie CAPTCHY. Sensowność jej wdrożenia polega na tym, iż jest to plugin do frameworka, a takowymi jeszcze się nie bawiłem (no chyba że skryptami/pluginami do wrzucenia aplikacji na serwer mor.ph).

Dla procesu rejestracji oraz późniejszego logowania stworzyłem w poprzednim wpisie kontroler Users. Teraz należy usunąć z niego wpis do scaffoldingu i zabrać się do samodzielnej pracy. Tworzymy akcję register, a także stosowny widok. Kod kontrolera jest oczywisty,  widok (registry.gsp) to też prosty formularz. Wkleiłbym kod (używam tam znaczników GSP), ale niestety WordPress wygrał ze mną i za cholery nie dało się mimo wpisania encji, etc. umieścić tego kodu tutaj w ładnej formie. Chyba czas przesiąść się na Joggera czy coś.

Pluginy do Grailsów możemy znaleźć na stronie domowej projektu. Ja postanowiłem użyć JCAPTCHA, polecanego w książce “Beginning Groovy and Grails”. Co prawda w treści wyżej wymienionej pozycji używa się prostszego rozwiązania, nie ma sensu przepisywać kodu, lepiej czegoś nowego się nauczyć. JCAPTCHA jest oficjalnym rozszerzeniem frameworka zatem wystarczy w konsoli wpisać (w katalogu naszej aplikacji):

grails install-plugin jcaptcha

By po chwili mielenia cieszyć się zaimportowanym rozszerzeniem. Tutaj dopiero zaczyna się przygoda. JCAPTCHA to nie w kij dmuchał i z nim trzeba już się trochę nagimnastykować. Wpierw trzeba wyedytować plik konfiguracyjny:

jcaptchas

{

image = new GenericManageableCaptchaService(

new GenericCaptchaEngine(

new GimpyFactory(

new RandomWordGenerator("abcdefghijklmnopqrstuvwxyz1234567890"),

new ComposedWordToImage(new RandomFontGenerator(

20, 30, [new Font("Arial", 0, 10)] as Font[]),

new GradientBackgroundGenerator(140, 35,

new SingleColorGenerator(Color.white),

new SingleColorGenerator(new Color(152, 245, 255))),

new NonLinearTextPaster(6, 6, new Color(108, 123, 139))

)

)

),

180,

180000)

}

Nie pytajcie co i z czym. Gotowca ściągnąłem z Disco Bloga. Dla mnie wystarczy, gdybym zaczął kombinować, aby tło było różowe, a literki czarne i do tego robiło BLING BLING co 2 sekundy to może bym się wgłębiał w meandry tego zapisu. Nie muszę to i nie zawracam sobie głowy. Na samym początku pliku należy dodać importy odpowiednich plików – lista pod ww. adresem. Dodać też trzeba linijkę w kodzie widoku, aby wrzucił nasz anty-spam do formularza:

<input type="text" id="captcha"  name="captcha" value=""/>

<jcaptcha:jpeg name="image"/>

Oto zdjęcie z efektem mojej pracy:

register_captcha1

Pozostaje tylko napisać kod kontrolera i logikę. Logika jest prosta – walidacja przekazanych danych i lecimy do przodu. Najistotniejszy wydaje się być kontroler, a także mechanizm do pokazywania błędów walidacyjnych (wrzucę to do layoutu by działał uniwersalnie wszędzie). Wymienione powyżej funkcjonalności głupio byłoby powielać w co drugim kontrolerze – choć i to jest metoda, przynajmniej w przypadku walidacji. Jednakże tutaj będę mądry i stworzymy takie coś jak service, czyli usługę. Jest to po prostu oddzielna klasa, która oferuje pewną funkcjonalność, a którą możemy łatwo wywołać z poziomu kontrolera. Co więcej – usługisingletonami (domyślnie) z całym dobrodziejstwem inwentarza.

Usługę utworzyć prosto – prawyklik na katalogu Services w NetBeans, wpisujemy nazwę klasy (u mnie formValidation) i mamy gotową usługę. Mocą konwencji do nazwy klasy jest dopisywany suffix ‘Service’. Oto wygenerowany kod:

class FormValidateService {boolean transactional = truedef serviceMethod() {}

}

W miejsce serviceMethod rzecz jasna trzeba wcisnąć nazwę metody, a także jej ciało. Kiedy tak się stanie mamy gotową na każde wezwanie usługę. Ja postanowiłem stworzyć dwie takowe – jedną uniwersalną do walidacji pól formularza, a także jeszcze jedną do translacji nazw pól (pole ‘name’ w elementach formularza) na polskie odpowiedniki celem ich ładnego wyświetlenia przy ew. błędzie walidacji. Oto kod usługi formValidate:

class FormValidateService {static int FIELD_INCORRECT = 0;

static int FIELD_COMPARE_INCORRECT = 1;

String fieldNamePl;

String fieldConfirmNamePl;

String defaultMessage;

boolean transactional = true

def translateFieldServiceHashMap validateForm( ArrayList Data ) {

HashMap dataValidated = new HashMap();

Boolean isValidated = true;

for( HashMap mapParams : Data ) {

if( mapParams.getAt('paramGiven') == mapParams.getAt('paramExpected') ) {

// dataValidated.putAt( mapParams.get('index'), true );

} else {

fieldNamePl = translateFieldService.translateName( mapParams.get('index') );

fieldConfirmNamePl = translateFieldService.translateName( mapParams.get('index_confirm') );

int compareStatus = mapParams.getAt('compareStatus');

if( compareStatus == FIELD_INCORRECT ) {

defaultMessage = 'Pole [' + fieldNamePl + '] ma niepoprawną wartość!';

}

else if( compareStatus == FIELD_COMPARE_INCORRECT ) {

defaultMessage = 'Pole [' + fieldNamePl + '] ma wartość inną niż pole [' + fieldConfirmNamePl + ']';

}

else {

defaultMessage = 'Pole [' + fieldNamePl + '] ma niepoprawną wartość!';

}

dataValidated.putAt( mapParams.get('index'), new org.springframework.validation.FieldError( 'FormValidateService', fieldNamePl, fieldNamePl, true, new String[1], new Object[1], defaultMessage ) );

isValidated = false;

}

}

return [ validated: isValidated , data: dataValidated ];

}

}

Jak już wspomniałem usługi to singletony (jest to zachowanie domyślne, ale jak wszystko w Grails daje się to zmienić), a odwoływanie się do nich jest banalne. Wystarczy w kontrolerze Users wpisać taki kod:

def formValidateService

I od tej pory do każdej z metod tejże usługi możemy odwoływać się z dowolnego miejsca w kontrolerze. Jak widać na powyższym przykładzie z poziomu kodu usługi możemy odwoływać się do funkcjonalności innej usługi. Oto jeszcze kod małej usługi tłumaczącej – można to oczywiście oprzeć o domyślne ustawienia i plikach z domyślnymi komunikatami. Póki co działa to w takiej formie, gdyż wówczas mamy dowolność – trzymania komunikatów np. w bazie danych, niekoniecznie zaś na plikach (co ułatwia często pracę). Kod:

class TranslateFieldService {boolean transactional = trueString translateName( String toTranslate ) {

String returnString = '';

HashMap translations = new HashMap();

translations.put( 'login', 'login' );

translations.put( 'passwd', 'hasło' );

translations.put( 'passwd_confirm', 'potwierdzenie hasła' );

returnString = ( translations.containsKey( toTranslate ) ) ? translations.getAt( toTranslate ) : toTranslate ;

return returnString;

}

}

I tyle nasze usługi. Powiedzmy, że dają one pewną funkcjonalność, choć z całą pewnością można by je ulepszyć. Dla naszej aplikacji ważne będzie też miejsce, gdzie możnaby wyświetlić informacje przeznaczone dla uzytkownika (błędy walidacji, informacje o charakterze neutralnym). By to osiągnąć dodałem trochę magii w pliku layoutu main.gsp, ale nie jest to nic wykraczającego poza zwykły IF. No i teraz nadszedł czas na kontroler:

class UsersController {def defaultAction = "index"

def formValidateService

def JcaptchaService

def index = { }

def register = {

if( session.user == null ) {

HashMap validationResults;

if( request.method == "POST" ) {

validationResults = formValidateService.validateForm(

[ [index: 'passwd', index_confirm: 'passwd_confirm',paramGiven: params.passwd.toLowerCase(),paramExpected: params.passwd_confirm.toLowerCase(), compareStatus: formValidateService.FIELD_COMPARE_INCORRECT ],

[index: 'captcha', paramGiven: JcaptchaService.validateResponse( 'image', session.id, params.captcha), paramExpected: true ] ] )

if( validationResults.getAt('validated') == true ) {

Htj_Users user = new Htj_Users( nick: params.nick, passwd: params.passwd.encodeAsMD5(), role: Htj_User_Role.get(1) );

if( user.save() ) {

session.user = user

flash.message = 'Rejestracja zakończona pomyślnie'

redirect(uri: '/')

} else {

flash.errors = user.errors.getAllErrors()

}

} else {

flash.errors = ((HashMap) validationResults.getAt('data')).values()

}

[ params: params ]

}

} else {

flash.message = 'Użytkownik już jest zalogowany!'

redirect( uri: '/' )

}

}

def login = {

if( session.user == null ) {

if( request.method == 'POST' ) {

Htj_Users user =

Htj_Users.findByNick(

params.login )

if( user != null ) {

session.user = user;

redirect( uri: '/' );

} else {

HashSet errors = new HashSet();

errors.add(new org.springframework.validation.FieldError( 'FormValidateService', 'login', 'login', true, new String[1], new Object[1], 'Podano złe dane!' ));

flash.errors = errors

}

}

} else {

flash.message = 'Już jesteś zalogowany!'

redirect( uri: '/' )

}

}

def logout = {

session.user = null;

flash.message = 'Zostałeś pomyślnie wylogowany!'

redirect( uri: '/' );

}

}

Jak przekonuję się po raz kolejny WordPress ma strasznie kijowe formatowanie kodu. Dlatego też sugeruję skopiowanie powyższego do IDE i przejrzenie kodu w bardziej przyjaznej formie. Myślę, że nie powinien on sprawić trudności. Poniżej kilka screenów z efektów działania programu:

Błąd rejestracji:

bledyrejestracji

Pomyślna rejestracja i zalogowanie:

zarejestrowanopomyslnie

To tyle. Oczywiście w realnie działającej aplikacji tak przeprowadzony proces rejestracji wołałby o pomstę do nieba – najlepiej byłoby dodać dla użytkowników pole e-mail, aby można było przesłać na wskazany adres mejl aktywujący (no i później w aplikacji wygodniej byłoby posiadać adresy swoich użytkowników). Również usability pewnikiem kuleje, podobnie jak sam kod :) Jednakże tak jakoś głupio wyszło, że napisanie tego posta zajęło mi dwa tygodnie więc podaruję sobie te małe błędy.

Marzec 30, 2009

Użyszkodnicy, czyli o co walczymy w każdym projekcie cz.I

Zaszufladkowany do: Grails, HowToJava — Tagi: , , — chlebik @ 11:54 pm

Zgodnie z zapowiedziami przyszedł czas na bardziej zaawansowane rzeczy w Grails. Myślę, że HowToJava jest gotowe na przyjęcie pierwszych użytkowników.

Zagadnienie zarządzania uzytkownikami każdej aplikacji internetowej to tzw. core, czyli element należący do rdzenia aplikacji, który po napisaniu podlega raczej niewielkim modyfikacjom. Dlatego też tym bardziej trzeba ten temat przemyśleć. Z całą pewnością kwestię użytkowników trzeba wpierw podzielić na 2 ‘fronty’ – backend i frontend.

Backendem w tym przpadku nazywam zarządzanie użytkownikami poprzez administrację lub automatyczne działania kodu. Czyli po rejestracji takowy kod wyśle mejla aktywującego, albo też wyświetli moderatorowi dane delikwenta, który pragnie zarejestrować się w serwisie. Z kolei frontend to wszystko to, co może zrobić użytkownik ze sobą (zarówno ten niezarejstrowany, jak i z pełnią praw w serwisie). Nie jest to podział intuicyjny, ani też taki, który da się wyczytać w necie. Jednakże dobrze oddaje kroki, które po kolei będę wykonywał, aby zaimplementować funkcjonalność w serwisie.

Wyjdźmy od modelu. Bardzo to trąci DDD, o którym w kontekście całości Grailsów z pewnością kiedyś napiszę. Jednakże to, że trąci wcale nie oznacza, że to źle! Dobra, bo zabieram się jak do zabicia karpia :) Oto co trzeba na pewno w aplikacji:

  • proces rejestrowania uzytkowników, a w nim:
    • formularz rejestracji, jego walidacja oraz zapis do bazy
    • poinformowanie użytkownika o powodzeniu/niepowodzeniu procesu rejestracyjnego ( e-mail, komunikaty na stronie zaraz po rejestracji)
  • stworzenie namiastki panelu administracyjnego, który pozwoli na zarządzanie użytkownikiem (zarówno w procesie rejestracji jak i jego dalszej działalności)
  • napisanie kodu, który obsłuży użytkownika po zalogowaniu na stronie (do tego oczywiście oddzielna funkcjonalność oparta na formularzu rejestracji), a konkretnie:
    • rejestracja użytkownika w zasięgu sesji aplikacji
    • zbudowanie mniej lub bardziej rozbudowanego ACLa
    • stworzenie funkcjonalności, w której użytkownik będzie mógł się poruszać (czyli reszty serwisu)

Jak już wcześniej wspomniałem piszę kolejne notki na bieżąco, zatem powyższa lista z pewnością jest niekompletna i ulegnie modyfikacjom. Da się jednakże zauważyć oczywistą rzecz – potrzebujemy na pewno stworzyć kod, który będzie reprezentował naszego użytkownika (lub kandydata na niego). Nie zamierzam póki co bawić się w bardziej zaawansowane rzeczy – rejestracja zatem będzie przebiegała po prostu poprzez wpisanie nicku (unikatowego), a także hasła (plus potwierdzenie), dodamy także delikatną CAPTCHE, aby spamerzy nie mieli zbyt łatwo. Antycypując kolejne kroki nadamy też każdemu użytkownikowi jakąś rolę, którą będzie miał w serwisie – na szybko będą to role pod tytułem ‘czytacz’, ‘zwykły user’ i ‘admin’ (to ja :). Oto klasa dziedzinowa dla użytkownika:

class Htj_Users {

    static mapping = {
        id column: 'user_id'
    }

     static constraints = {
         nick( blank: false, size: 5..40, nullable: false, unique: true )
         passwd(blank: false, maxSize: 32, nullable: false, password: true )
         role( blank: false, nullable: false )
     }

    String nick;
    String passwd;
    Htj_User_Role role;
}

No i oczywiście mikroskopijna klasa dla typu roli:

class Htj_User_Role {

  static mapping = {
      id column: 'role_id'
  }

  String role_name;
}

Tworzymy kontroler o wdzięcznej nazwie UserController. Wpisujemy do niego taki kod:

def scaffold = Htj_Users;

i można już pobawić się dzięki scaffoldingowi. Ja do bazy przez PMA dodałem tylko trzy kategorie (wcześniej wymienione) i jak zawsze rusztowanie pozwala rozeznać się czy wszystko gra. Póki co gra zatem na dziś to koniec, późno trochę. Następnym razem zaimplementujemy cały CRUD ‘z palca’ plus zainstalujemy i wykorzystamy pierwszy raz plugin do frameworka.

Starsze wpisy »

Blog na WordPress.com.