Stronnice Chlebika – Java Blog for Newbies

listopad 5, 2009

Rejestrowanie użytkowników w ProgramBash – część 2

Zaszufladkowany do: Hibernate, JavaServer Faces, ProgramBash — chlebik @ 12:47 am

Wracając do poprzedniego wpisu – rejestracja to rzecz dość istotna. Dla przypomnienia – na razie zasadniczo poza wygenerowaniem formularza i napisaniem od cholery kodu , który nie za wiele jest w stanie zrobić, przyszedł czas na coś bardziej spektakularnego.

Zaczniemy od walidacji. Zawsze uważałem, że bawienie się w bajery w stylu walidacji po stronie klienta (JavaScript) to tak naprawdę strata czasu – i tak ostatecznie walidacja po stronie serwera będzie miała miejsce, zaś ilość potencjalnych danych, które można źle wpisać nie jest aż tak wielka, aby uznać ewentualny request do serwera za jakoś specjalnie drogi. Dlatego też skupię się tylko na mechanizmach walidacyjnych wbudowanych w sam mechanizm JSF. Poki co mamy do czynienia z takim oto formularzem:

formularz

Co powinno być walidowane? Ano z całą pewnością nick i adres e-mail. Załóżmy, że to e-mail jest identyfikującą składową dla naszych użytkowników i przez to musi być unikalny. Do tego adres e-mail to adres e-mail. Musi spełniać wymogi poprawnego formatu (opisanego w którymś z RFC) – tutaj bez wyrażeń regularnych raczej się nie obejdzie. Długość nicka oraz haseł jest zależna od nas samych – dodamy jednakże ograniczenie minimum jak i maksimum dla tych wartości.

Jednakże zanim to nastąpi należy zastanowić się nad formą wyświetlania komunikatów walidacyjnych. W praktyce stosuje się dwa podejścia – albo listuje się stosowne komunikaty na górze strony, albo też wyświetla się wszystkie po kolei obok walidowanego pola/wartości. Myślę, że to drugie podejście jest o tyle bardziej odpowiednie, iż od razu wiadomo gdzie coś poszło nie tak. Przeglądanie rozbudowanej listy błędów walidacji nie jest zbyt dobrym podejściem, zwłaszcza kiedy nagle komunikaty zajmują połowę ekranu. To jedna sprawa. Druga zaś dotyczy samej treści komunikatów. Nie podchodzę nawet do zagadnienia internacjonalizacji aplikacji, gdyż byłyby to perły rzucane przed wieprze i nawet edukacyjny charakter projektu nie usprawiedliwiałby takich ozdobników. Jednakże posiadanie absolutnie unikalnych i zgromadzonych w jednym miejscu komunikatów jest w każdej aplikacji dość istotne. Dlatego też napiszę kilka słów o pakietach komunikatów.

Podobną rzecz opisywałem już wcześniej przy okazji projektu HowToJava. Jednakże w przypadku Grails istnieją już tam gotowe do wypełnienia pliki komunikatów – wystarczy pobawić się w copywritera i gotowe. W przypadku JSF wymaga to troszeczkę więcej pracy. Podstawą jest rzecz jasna stworzenie pliku komunikatów. W przypadku mojej aplikacyjki dorzuciłem takowy plik (msg.properties) do pakietu z klasami narzędziowymi. Proste. Oczywiście nasz pakiet musimy dopisać do konfiguracji w pliku faces-config.xml. Robimy to w ten oto sposób:

<application>
     <message-bundle>
        com.wordpress.chlebik.util.msg
    </message-bundle>

    <resource-bundle>
        <base-name>com.wordpress.chlebik.util.msg</base-name>
        <var>msgs</var>
    </resource-bundle>
  </application>

Dlaczego wpisujemy dwa razy ten sam plik? Albowiem przy pominięciu pierwszej definicji ( tej z message-bundle ) nie będą działały nadpisania dla standardowych komunikatów! Warto o tym wiedzieć, mi zajęło to parę minut rzucania mięchem :) Samo posiadanie pliku nie jest rzecz jasna do niczego przydatne. Należy jeszcze zaimplementować klasę, która będzie potrafiła coś z naszego pliku wyciągnąć. I tutaj zaczynają się schody. Wyciągnięcie bowiem czegokolwiek z takiego pliku wymaga bowiem trochę zabawy – mianowicie należy uzyskąc referencję do bieżących ustawień regionalnych, potem mechanizm ładowania klas (by zlokalizować nasz plik z komunikatami), zaś na końcu otrzymać dostęp do właściwego pakietu dla wybranych ustawień regionalnych. Wyciągnięcie w tym momencie konkretnego komunikatu to już fraszka. Ufff. Trochę tego za dużo. Dlatego też zgodnie z zasadą DRY zamierzam użyć kodu, który autorzy mojej książki o JSF łaskawie udostępnili dla szerokiej rzeszy programistów. Oto on:

package com.wordpress.chlebik.util;

import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;

public class Messages {
   public static FacesMessage getMessage(String bundleName, String resourceId,
      Object[] params) {
      FacesContext context = FacesContext.getCurrentInstance();
      Application app = context.getApplication();
      String appBundle = app.getMessageBundle();
      Locale locale = getLocale(context);
      ClassLoader loader = getClassLoader();
      String summary = getString(appBundle, bundleName, resourceId,
         locale, loader, params);
      if (summary == null) summary = "???" + resourceId + "???";
      String detail = getString(appBundle, bundleName, resourceId + "_detail",
         locale, loader, params);
      return new FacesMessage(summary, detail);
   }

   public static String getString(String bundle, String resourceId,
         Object[] params) {
      FacesContext context = FacesContext.getCurrentInstance();
      Application app = context.getApplication();
      String appBundle = app.getMessageBundle();
      Locale locale = getLocale(context);
      ClassLoader loader = getClassLoader();
      return getString(appBundle, bundle, resourceId, locale, loader, params);
   }

   public static String getString(String bundle1, String bundle2,
         String resourceId, Locale locale, ClassLoader loader,
         Object[] params) {
      String resource = null;
      ResourceBundle bundle;

      if (bundle1 != null) {
         bundle = ResourceBundle.getBundle(bundle1, locale, loader);
         if (bundle != null)
            try {
               resource = bundle.getString(resourceId);
            } catch (MissingResourceException ex) {
            }
      }

      if (resource == null) {
         bundle = ResourceBundle.getBundle(bundle2, locale, loader);
         if (bundle != null)
            try {
               resource = bundle.getString(resourceId);
            } catch (MissingResourceException ex) {
            }
      }

      if (resource == null) return null; // brak dopasowania
      if (params == null) return resource;

      MessageFormat formatter = new MessageFormat(resource, locale);
      return formatter.format(params);
   }

   public static Locale getLocale(FacesContext context) {
      Locale locale = null;
      UIViewRoot viewRoot = context.getViewRoot();
      if (viewRoot != null) locale = viewRoot.getLocale();
      if (locale == null) locale = Locale.getDefault();
      return locale;
   }

   public static ClassLoader getClassLoader() {
      ClassLoader loader = Thread.currentThread().getContextClassLoader();
      if (loader == null) loader = ClassLoader.getSystemClassLoader();
      return loader;
   }
}

Listing nie jest specjalnie skomplikowany – to po prostu żonglerka dużymi partiami jakże często powtarzającego się kodu. Dla programisty istotne jest to, iż by wyciągnąć jakikolwiek komunikat wystarczy użyć statycznej metody getMessage przekazując jej odpowiednie parametry, aby otrzymać stosowny komunikat. Mogłoby to wyglądać na przykład tak:

FacesMessage msg = com.wordpress.chlebik.util.Messages.getMessage ( "com.wordpress.chlebik.msg", "niepoprawnyFormatAdresuEmail", null );

Podanie wartości NULL jako trzeciego parametru dla metody wskazuje na fakt, iż w komunikacie nie będą występowały symbole zastępcze (o nich później). Zaś gdybyśmy jednak chcieli by wystąpiły, wówczas należy przekazać do metody tablicę Object[]. Jak widać tę klasę również trzeba było wyszczególnić w pliku konfiguracyjnym!

Teraz zajmiemy się pisaniem własnego walidatora. Standardowe walidatory JSF nadają się do prozaicznych rzeczy – sprawdzania czy dana wartość została wpisana, walidacji długości łancucha i tym podobnych rzeczy. Same w sobie są to rzeczy całkiem potrzebne i używane zasadniczo bardzo często. Jednakże nie w tym rzecz by zadowalać się prostymi rozwiązaniami. Wszak na walidację czeka adres e-mail, chyba jeden z najbardziej “przerobionych” walidatorów na świecie. Samo wyrażenie regularne, które będzie walidowało wpisaną wartość ściągniemy od innych, natomiast ja podam przepis jak opakować porównanie w naszą klasę walidującą.

Wszystkie niestandardowe walidatory muszą implementować interfejs javax.faces.validator.Validator, a także zostać dodane do pliku faces-config.xml. Implementacja interfejsu polega na nadpisaniu tylko jednej metody – validate(), która w razie poprawnej walidacji nie zwraca nic, natomiast w przeciwnym wypadku zgłasza wyjątek ValidatorException z komunikatem błędu wyciągniętym z naszego pakietu komunikatów. Oto klasa walidująca:

public class EmailValidator implements Validator {

    /**
     * Metoda walidujaca
     *
     */
    public void validate(FacesContext context, UIComponent component, Object value) {

        if( value == null ) return;
        if( !(value instanceof String) ) return;

        if( !checkEmail( (String) value) )  {
            FacesMessage msg = com.wordpress.chlebik.util.Messages.getMessage( "com.wordpress.chlebik.util.msg" , "emailNotValid", null);
            msg.setSeverity( FacesMessage.SEVERITY_ERROR );
            throw new ValidatorException( msg );
        }
    }

    /**
     * Prywatna metodka gdzie mielimy temat
     *
     * @param  String   email
     * @return boolean
     */
    private boolean checkEmail( String email ) {

      Pattern p = Pattern.compile(".+@.+\\.[a-z]+");
      Matcher m = p.matcher(email);

      boolean result = m.matches();

      if ( result )
        return true;
      else
        return false;
    }

}

By zaś nie przedłużać – oto kod formularza rejestracyjnego z dodanymi wbudowanymi walidatorami JSF.

<fieldset>
		<legend>Podaj swoje dane</legend>
			<div>
                            <label for="nick">Nick</label>
                            <h:inputText value="#{user.nick}" id="nick" required="true" requiredMessage="#{msgs.nickRequired}">
                                <f:validateLength maximum="60" minimum="3" />
                            </h:inputText>
                            <h:message for="nick" errorClass="error" />
                        </div>

			<div>
                            <label for="email">E-mail</label>
                            <h:inputText value="#{user.email}" id="email" required="true"  requiredMessage="#{msgs.emailRequired}">
                                <f:validateLength maximum="256" />
                                <f:validator validatorId="com.wordpress.chlebik.Email" />
                            </h:inputText>
                            <h:message for="email" errorClass="error"/>
                        </div>

                        <div>
                            <label for="pass">Hasło</label>
                            <h:inputSecret value="#{user.pass}" id="pass"  required="true"  requiredMessage="#{msgs.passRequired}"/>
                            <h:message for="pass" errorClass="error"/>
                        </div>

                        <div>
                            <label for="passconfirmation">Powtórz hasło</label>
                            <h:inputSecret value="#{user.passconfirmation}" id="passconfirmation"  required="true"  requiredMessage="#{msgs.passconfRequired}"/>
                            <h:message for="passconfirmation" errorClass="error"/>
                        </div>

                        <div>
                            <label for="sex">Płeć</label>
                            <h:selectOneMenu value="#{user.sex}" id="sex" >
                                <f:selectItems value="#{sexBean.sexes}" />
                            </h:selectOneMenu>
                        </div>

	</fieldset>

Wysłanie źle wypełnionego formularza powoduje ponowne wyświetlenie strony wraz z komunikatami o błędach. Komunikaty te są składowane w pliku msg.properties, który wygląda w ten sposób (podano komunikaty walidacji, a także nadpisane standardowe powiadomienia).

javax.faces.component.UIInput.REQUIRED=Wypełnienie tego pola jest obowiązkowe!
javax.faces.validator.LengthValidator.MAXIMUM=Podana wartość jest dłuższa niż {0} znaków
javax.faces.validator.LengthValidator.MINIMUM=Podana wartość jest krótsza niż {0} znaki
nickRequired=Podanie nicka jest obowiązkowe!
emailRequired=Podanie adresu e-mail jest obowiązkowe!
passRequired=Podanie hasła jest obowiązkowe!
passconfRequired=Potwierdzenie hasła jest obowiązkowe!
emailNotValid=Podany adres e-mail jest niepoprawny!

I tak mniej więcej może wyglądać nasz formularz po wypełnieniu go złymi danymi:

walidacja

Jednakże kiedy nasz formularz przejdzie już walidację – dotrze on ostatecznie do klasy UserController. Ona z kolei bieże na siebie kwestię jeszcze jednej walidacji (czy oba hasła wpisane w formularzu są zgodne), po czym w zależności od wyniku zapisuje nowy rekord w bazie danych, albo też ponownie wyświetla formularz z błędem walidacji. Oto kod:

public class UserController {

    private User user;

   /**
    * Akcja obslugujaca rejestracje w systemie
    *
    * @return User
    * @author Michał Piotrowski
    */
    public String register() {

        FacesContext context = FacesContext.getCurrentInstance();
        Map params = context.getExternalContext().getRequestParameterMap();

        String pass       =    (String) params.get("pass");
        String passconf   =    (String) params.get("passconfirmation");
        String nick       =    (String) params.get("nick");
        String email      =    (String) params.get("email");
        Date addDate = new Date();

        if( pass.equals( passconf ) )  {

            try {

                pass = MD5.MD5(pass);
                User newUser = new User( null, nick, email, pass, "/bedzie/path/do/pliku", Math.round(addDate.getTime()/1000), 0, SexEnum.valueOf( (String) params.get("sex") ) );

                SessionFactory sessionFactory = com.wordpress.chlebik.util.ProgramBashUtil.getSessionFactory();
                Session session = sessionFactory.openSession();
                session.save( newUser );

                context.addMessage( null, new FacesMessage( FacesMessage.SEVERITY_INFO, "Rejestracja zakończyła się sukcesem!", "") );
             }
             catch( Exception e ) {
                 context.addMessage( null, new FacesMessage( FacesMessage.SEVERITY_ERROR, "Rejestracja nie powiodła się!", "") );
                 return "register-failure";
             }

             return "registered";
        }
        else {
             FacesMessage msg = com.wordpress.chlebik.util.Messages.getMessage( "com.wordpress.chlebik.util.msg" , "passconfFailed", null);
             msg.setSeverity( FacesMessage.SEVERITY_ERROR );
             context.addMessage("passconfirmation", msg );
             return "register-failure";
        }

    }
}

Kod jest dość oczywisty. Wpierw odczytujemy z mapy parametrów przekazanych w tabeli POST potrzebne nam wartości. Porównujemy hasła, a na końcu tworzymy obiekt i zapisujemy go w bazie. Dorzucamy także stosowny komunikat w zależności od tego, czy rejestracja zakończyła się sukcesem czy nie. W pliku layoutu dorzuciłem taki oto mały HTML:

 <div id="messagesWrapper">
            <h:messages globalOnly="true" infoClass="flashMessageInfo" errorClass="flashMessageError" />
 </div>

Parę zmian w CSSie i pięknie wszystko śmiga. Ah byłbym zapomniał. W regule nawigacji w pliku faces-config w przypadku powodzenia operacji by wyświetlił się nam komunikat, należy wyrzucić znacznik <redirect />. Jeśli tego nie zrobimy nasz komunikat nie zostanie wyświetlony, gdyż po fazie wizualizacji odpowiedzi zostanie wysłany kolejny request. Rozwiązanie to nastręcza jednakże pewien problem, a mianowicie ponownego wysłania tych samych danych. Tym jednakże zajmiemy się następnym razem. Póki co cieszę się z istniejącego (w bardzo skromnym wymiarze) rejestrowania użytkowników.

 

walidacjaSukces

listopad 4, 2009

[SPAM] O pewnym nowym serwisie

Zaszufladkowany do: Uncategorized — chlebik @ 4:27 pm

Zasadniczo kiedy już publikuję coś na tematy programistyczne, nie wychodzę poza ściśle interesujące mnie rzeczy. W skrócie zatem – Java i wszystkie jej pochodne. Dziś jednakże zrobię wyjątek.

Rzecz dotyczy nowego serwisu (o którym jak sami autorzy piszą – “odkrywczy nie jest”). Jednakże jest to rodzima inicjatywa i mam nadzieję, że z czasem się rozwinie na tyle, aby stać się pewnym wspólnym miejscem dla wszystkich ludzi związanych z IT w tym kraju. Pisałem już kiedyś, że takie miejsce nie do końca istnieje dla Javalandu (owszem są grupy dyskusyjne i kilka mniej lub bardziej popularnych for), zaś inne języki takowe w naszym kraju posiadają (na pewno PHP, Ruby i Pytonik). By nie przedłużać – http://www.develway.pl. Sami oceńcie czy coś z tego wyniknie.

listopad 3, 2009

Odwieszenie bloga czyli internet wszędzie taki sam

Zaszufladkowany do: Blog, Life — chlebik @ 4:13 pm

Oficjalnie jestem już przeprowadzony. Kosztowało to duuuużo czasu, nie mniej pieniędzy i wysiłku. Jednakże jest to fakt dokonany – jestem oficjalnie mieszkańcem Pruszkowa, a także co więcej – czas na odwieszenie bloga. Już niedługo pojawi się kolejny materiał o ProgramBash – rejestracji użytkowników ciąg dalszy. Co potem – zobaczymy. Jak zawsze doba jest za krótka w stosunku do chęci i zapału.

październik 23, 2009

Zawieszenie bloga

Zaszufladkowany do: Blog, Life — chlebik @ 12:54 am

W związku z przeprowadzką oraz remontem zawieszam prowadzenie bloga na okres mniej więcej dwóch tygodni.

Co prawda miałem jeszcze nadzieję, że zdołam napisać drugą część materiału o rejestrowaniu użytkowników w ProgramBash, jednakże fizycznie nie dałem rady. W związku z czym materiał sobie poczeka. Przeprowadzam się do Pruszkowa (tak, tam gdzie kończą się Reguły :) – dostęp do sieci oferuje firma, która wygląda na w miarę porządną. Obiecują 4 Mbit i zewnętrzne IP dostarczyć w kablu w przeciągu kilku dni, zatem istnieje opcja, że w następną niedzielę poranną kawę wypiję kończąc wyżej wspomniany wpis. Istnieje jednakże opcja, że będzie inaczej w związku z czym zakładam bezpieczny termin 2 tygodni. Do przeczytania.

październik 16, 2009

Rejestrowanie użytkowników w ProgramBash – część 1

Zaszufladkowany do: JavaServer Faces, ProgramBash — chlebik @ 12:00 am

Wokół użytkowników kręci się cały ten internetowy biznes. Oczywistą zatem rzeczą jest obecnie implementacja procesu rejestracji użytkowników, a także ich logowania do aplikacji, co daje rzecz jasna o wiele większe możliwości niż bierne przeglądanie serwisu. Do dzieła.

Oczywiście jak każą dobre praktyki zaczniemy od modelu. Podobny proces już przerabiałem w przypadku HowToJava, a zgodnie z zasadą DRY wypadałoby cofnąć się do doświadczeń z poprzedniego projektu. To pierwsze założenie. Drugie jednakoż mówi o tym, aby w każdym kolejnym projekcie posuwać się dalej niż w poprzednim, zrobić coś bardziej ciekawego i zaawansowanego. I tak będzie tym razem. Poza standardowymi danymi użytymi w HowToJava planuję zaimplementować jeszcze następujące funkcje:

  • avatary użytkowników – rzecz jasna wraz z wgrywaniem plików. Sarkałem na obsługę uploadu plików w JSF, teraz będę miał okazję rzecz sprawdzić w praktyce.
  • określanie płci – to nie jest żart! Konkretnie możnaby w mapowaniu tabeli bazy danych zrobić rzecz na zwykłym SMALLINT i zgodzić się, że 1 to panie, a 2 panowie. Ale to nie bardzo profesjonalne. Poruszę temat typów wyliczeniowych w mapowaniu Hibernate i zobaczymy co z tego wyniknie.
  • kody aktywacyjne – w przypadku HowToJava rzecz była bardzo prosta, gdyż ograniczała się do wpisania adresu poczty elektronicznej, dwa razy hasła no i rzecz jasna CAPTCHY. Niektórych jednakże ta ostatnia niemożliwie denerwuje, zatem tym razem pobawię się w wysyłanie mejli wraz z kodami aktywacyjnymi.

Założenia dość interesujące, zatem trzeba zabrać się do dzieła. Postanowiłem zacząć od rozeznania tematu związanego z typami wyliczeniowymi (ENUM), zarówno jako konstrukcji języka, jak i typu w bazie danych MySQL.

Dla przypomnienia – typ wyliczeniowy w MySQL to specjalny typ kolumny przeznaczony do przechowywania ściśle określonych typów. Warto wspomnieć, iż MySQL konwertuje sobie zawartość takiej kolumny na odwzorowania typu INT. Co za tym idzie znacząco przyspiesza wyszukiwanie po tejże kolumnie, pomimo pozorów używania wartości tekstowych. Jeśli zaś chodzi o ENUM w Javie, jest to rzecz wprowadzona wraz z pojawieniem się wersji 1.5 języka. Temat jest dość obszerny, zatem zainteresowanych odsyłam na strony tutoriala firmy SUN. Użycie tego typu kolumny nasuwa się od razu, kiedy będziemy operować na ledwie kilku potencjalnych wartościach. Czyli określenie płci ( ‘m’,'f’ ), statusu konta ( ‘banned’,'active’,'deleted’,'awaiting’ ) czy choćby poziomu uprawnień ( ‘admin’, ‘user’, ‘guest’ ) to podręcznikowe wręcz przyłady sensowności implementacji typu wyliczeniowego. W przypadku użytkowników ProgramBash skupimy się na dwóch wymienionych powyżej typach – płci oraz statusie konta.

Konwersacja z wujkiem G zaowocowała dwoma linkami – blogiem dangertree, a także znajdującym się tam odnośnikiem do oficjalnej dokumentacji Hibernate.
Rzecz jest dość prosta – musimy stworzyć własny typ wyliczeniowy, aby dało się go zastosować jako typ składowej obiektu odwzorowania. Kiedy będziemy już mieli typ wyliczeniowy, wówczas stworzymy plik odwzorowań dla Hibernate, zaś na końcu stworzymy gotowy obiekt.

Oto kod dla naszego typu wyliczeniowego:

public enum SexEnum {
    MALE(0), FEMALE(1);

    private int id;

    private SexEnum(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public static SexEnum valueOf(int id) {
        switch (id) {
            case 0: return MALE;
            case 1: return FEMALE;
            default: return MALE;
        }
    }
}

Okazuje się jednakże, że to nie wszystko. Potrzeba jeszcze stworzyć klasę, która będzie imlpementowała interfejs UserType oraz ParameterizedType, które są potrzebne naszemu ORMowi. Kod dość długi:

public class SexEnumUserType implements UserType, ParameterizedType {

    private Class<? extends Enum> enumClass;
    private Class<?> identifierType;

    private Method identifierMethod;
    private Method valueOfMethod;

    private static final String defaultIdentifierMethodName = "getId";
    private static final String defaultValueOfMethodName = "valueOf";

    private NullableType type;
    private int [] sqlTypes;

    public void setParameterValues(Properties parameters) {
        String enumClassName = parameters.getProperty("enumClass");
        try {
            enumClass = Class.forName(enumClassName).asSubclass(Enum.class);
        }
        catch (ClassNotFoundException exception) {
            throw new HibernateException("Enum class not found", exception);
        }

        String identifierMethodName =
            parameters.getProperty("identifierMethod", defaultIdentifierMethodName);

        try {
            identifierMethod = enumClass.getMethod(identifierMethodName, new Class[0]);
            identifierType = identifierMethod.getReturnType();
        }
        catch(Exception exception) {
            throw new HibernateException("Failed to optain identifier method", exception);
        }

        type = (NullableType)TypeFactory.basic(identifierType.getName());

        if(type == null)
            throw new HibernateException("Unsupported identifier type " + identifierType.getName());

        sqlTypes = new int [] {type.sqlType()};

        String valueOfMethodName =
            parameters.getProperty("valueOfMethod", defaultValueOfMethodName);

        try {
            valueOfMethod = enumClass.getMethod(
                    valueOfMethodName, new Class[] { identifierType });
        }
        catch(Exception exception) {
            throw new HibernateException("Failed to optain valueOf method", exception);
        }
    }

    public Class returnedClass() {
        return enumClass;
    }

    public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
                        throws HibernateException, SQLException {
        Object identifier=type.get(rs, names[0]);
        try {
            return valueOfMethod.invoke(enumClass, new Object [] {identifier});
        }
        catch(Exception exception) {
            throw new HibernateException(
                    "Exception while invoking valueOfMethod of enumeration class: ", exception);
        }
    }

    public void nullSafeSet(PreparedStatement st, Object value, int index)
            throws HibernateException, SQLException {
        try {
            Object identifier = value != null ? identifierMethod.invoke(value, new Object[0]) : null;
            st.setObject(index, identifier);
        }
        catch(Exception exception) {
            throw new HibernateException(
                    "Exception while invoking identifierMethod of enumeration class: ", exception);

        }
    }
    public int[] sqlTypes() {
        return sqlTypes;
        //There was a logical bug within the set-up phase of any user type
        //I reported the issue and it got instantly solved (Thanks again Garvin!)
        //But it might still exist in your Hibernate version. So if you are
        //facing any null-pointer exceptions, use the return statement below.
        //Note: INTEGER works even for String based mappings...
        //return new int [] {Types.INTEGER};
    }

        public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return cached;
    }

    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable)value;
    }

    public boolean equals(Object x, Object y) throws HibernateException {
        return x==y;
    }

    public int hashCode(Object x) throws HibernateException {
        return x.hashCode();
    }

    public boolean isMutable() {
        return false;
    }

    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return original;
    }
}

Oto jak wygląda kod klasy odwzorowującej dla Hibernate:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="hibernate.mappings">

    <typedef class="com.wordpress.chlebik.mappings.enums" name="sexEnum">
        <param name="enumClassName">com.wordpress.chlebik.mappings.enums.SexEnum</param>
        <param name="identifierMethod">getId</param>
    </typedef>

  <class name="com.wordpress.chlebik.User" table="users">
    <id column="id" name="id" type="long">
      <generator class="native"/>
    </id>
    <property column="adddate" name="adddate" type="integer"/>
    <property column="registerdate" name="registerdate" type="integer"/>
    <property column="avatarpath" name="avatarpath" type="string"/>
    <property column="email" name="email" type="string"/>
    <property column="nick" name="nick" type="string"/>
    <property column="pass" name="pass" type="string"/>
    <property column="sex" name="sex" type="sexEnum"/>
  </class>
</hibernate-mapping>

Kilka słów wyjaśnienia. addDate różni się od registerDate tym, iż ta pierwsza jest datą pojawienia się konkretnego rekordu w bazie danych, zaś registerDate jest zapisaną datą aktywacji konta. Jak widać na początku pliku odwzorowania trzeba było poinformować Hibernate o tym, iż w użyciu będzie nasz specjalny typ (deklaracja typedef).

Na sam koniec kod klasy reprezentującej naszego użytkownika:

@Entity
@Table(name = "users")
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String nick;
    private String email;
    private String avatarpath;
    private Integer adddate;
    private Integer registerdate;
    private SexEnum sex;
    private String pass;
    private String passconfirmation;

    public User() {
    }

    public User(String nick, String email, String pass, String passconfirmation, String avatarpath, Integer adddate, Integer registerdate, SexEnum sex) {
        this.nick = nick;
        this.pass = pass;
        this.passconfirmation = passconfirmation;
        this.email = email;
        this.avatarpath = avatarpath;
        this.adddate = adddate;
        this.registerdate = registerdate;
        this.sex = sex;
    }

    // Gettery
    public String getNick() {
        return nick;
    }

    public String getPass() {
        return pass;
    }

    public String getPassconfirmation() {
        return passconfirmation;
    }

    public String getEmail() {
        return email;
    }

    public String getAvatarPath() {
        return avatarpath;
    }

    public Date getAdddate() {
        Date dateDateObject = new Date();
        long dateInMilis = ((long) adddate) * 1000;
        dateDateObject.setTime(dateInMilis);
        return dateDateObject;
    }

    public Date getRegisterdate() {
        Date dateDateObject = new Date();
        long dateInMilis = ((long) registerdate) * 1000;
        dateDateObject.setTime(dateInMilis);
        return dateDateObject;
    }

    public SexEnum getSex() {
        return sex;
    }

    protected Long getId() {
        return id;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void setSex(SexEnum sex) {
        this.sex = sex;
    }

    public void setNick(String nick) {
        this.nick = nick;
    }

    public void setPass(String pass) {
        this.pass = pass;
    }

    public void setPassconfirmation(String passconfirmation) {
        this.passconfirmation = passconfirmation;
    }

    public void setRegisterdate(Integer registerdate) {
        this.registerdate = registerdate;
    }

    public void setAdddate(Integer adddate) {
        this.adddate = adddate;
    }

    public void setAvatarpath(String avatarpath) {
        this.avatarpath = avatarpath;
    }
}

Tak to wygląda póki co. Teraz jednakże wypada stworzyć trochę kodu, który umożliwi nam wprowadzenie stosownych danych do aplikacji. Po małych zabawach z CSSem i HTMLem do odpowiedniego pliku widoku dorzuciłem taki oto kod:

<%@taglib uri="http://richfaces.org/a4j" prefix="a4j"%>
<%@taglib uri="http://richfaces.org/rich" prefix="rich"%>

<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>

<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>

<div class="form-container">

	<h:form>

	<fieldset>
		<legend>Podaj swoje dane</legend>
			<div>
                            <label for="nick">Nick</label>
                            <h:inputText value="#{user.nick}" id="nick" />
                        </div>

			<div>
                            <label for="email">E-mail</label>
                            <h:inputText value="#{user.email}" id="email" />
                        </div>

                        <div>
                            <label for="pass">Hasło</label>
                            <h:inputSecret value="#{user.pass}" id="pass" />
                        </div>

                        <div>
                            <label for="pass_confirmation">Powtórz hasło</label>
                            <h:inputSecret value="#{user.pass_confirmation}" id="pass_confirmation" />
                        </div>

                        <div>
                            <label for="sex">Płeć</label>
                            <h:selectOneMenu value="#{user.sex}" id="sex" >
                                <f:selectItems value="#{sexBean.sexes}" />
                            </h:selectOneMenu>
                        </div>

<!--   Inne pola póki co pominąłem, chodzi o pokazanie tematu -->

	</fieldset>

	<div class="buttonrow">
            <h:commandButton type="submit" value="Zarejestruj" id="register" action="#{userController.register}"  />
	</div>

        </h:form>
</div>

Rzecz jest wstępnie skrócona do podstawowych własności by zobaczyć czy nasz przykład działa. Jak widać w znaczniku <h:commandButton> podajemy akcję zaimplementowaną w klasie userController. Albowiem gdzieś te nasze requesty muszą zostać obsłużone. Bean o nazwie user będzie nam służył do reprezentacji obiektu obecnie zalogowanego użytkownika.  Plik faces-config.xml dla aplikacji wygląda póki co tak:

<managed-bean>
  <managed-bean-name>newsBean</managed-bean-name>
  <managed-bean-class>com.wordpress.chlebik.beans.NewsBean</managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
 </managed-bean>

 <managed-bean>
  <managed-bean-name>userController</managed-bean-name>
  <managed-bean-class>com.wordpress.chlebik.controllers.UserController</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
 </managed-bean>

  <managed-bean>
  <managed-bean-name>user</managed-bean-name>
  <managed-bean-class>com.wordpress.chlebik.User</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
 </managed-bean>

 <managed-bean>
  <managed-bean-name>sexBean</managed-bean-name>
  <managed-bean-class>com.wordpress.chlebik.beans.SexBean</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
 </managed-bean>

<navigation-rule>
    <from-view-id>/rejestruj.jsp</from-view-id>
    <navigation-case>
        <from-outcome>registered</from-outcome>
        <to-view-id>/glowna.jsp</to-view-id>
        <redirect />
    </navigation-case>
    <navigation-case>
        <from-outcome>register-failure</from-outcome>
        <to-view-id>/rejestruj.jsp</to-view-id>
    </navigation-case>
</navigation-rule>

Jak widać poza klasami zajmującymi się logiką, w pliku konfiguracyjnym umieściłem reguły nawigacji. Mówią one jedno – z widoku o adresie /rejestruj.jsp (w regułach podajemy nazwy pliku widoku, a nie mapowania np. rejestruj.faces będzie błędne) należy w razie powodzenia przejść do widoku strony głównej (poprzez REDIRECT), zaś w przypadku pomyłki należy ponownie wyświetlić formularz rejestracji. Kod naszego kontrolera na razie będzie przykładowy – akcja register będzie po prostu zwracała wartość ‘registered’.

public class UserController {

   /**
    * Akcja obslugujaca rejestracje w systemie
    *
    * @return String
    * @author Michał Piotrowski
    */
    public String register()   {
       return "registered";
    }
}

Uruchamiamy aplikację, przechodzimy pod adres rejetruj.faces i naszym oczom pokazuje się śliczny formularz rejestracyjny. Niezależnie od tego czy wpiszemy cokolwiek w pola formularza czy też nie, po naciśnięciu przycisku ‘REJESTRUJ’ zostaniemy przekierowani na główną stronę aplikacji. Póki co rzecz jest posta jak konstrukcja gwoździa, jednakże wpis stał się cokolwiek za długi, zatem kolejna część następnym razem. Spróbujemy zwalidować nasz formularz oraz zapisać coś do bazy.

październik 15, 2009

Sprzęt to podstawa

Zaszufladkowany do: Life — chlebik @ 8:14 am

Podstawa. I dlatego ostatnio ucichło na blogu – padł mi monitor. No może nie od razu umarł, ale zaczął wyczyniać cuda niewidy.

No i stąd konieczność nowego zakupu – mój wysłużony CRT Sony odstawiłem na bok, zaś na moim biurku pojawił się taki oto wynalazek:

iiyama-ProLite-E2407HDSV

Jak na razie spisuje się świetnie, na biurku przybyło mi miejsca, a ja mam nadzieję, że nowe wpisy pojawią się już niedługo.

październik 5, 2009

Java exPress, a sprawa Chlebika po raz drugi

Zaszufladkowany do: SCJP — chlebik @ 9:35 pm

Tadam! Po miesięcznym poślizgu wydawniczym “nadejszła wielkopomna chwila” jak mówił pewien znany człowiek – pojawił się kolejny numer Java exPress!

Co w tym takiego wielkiego? Ano to, iż znaleźć tam można artykuł, który popełniłem jakiś czas temu, a który jest kompilacją wpisów dotyczących SCJP. Wszystko zebrane w jednym miejscu, ładnie poskładane i wydane. Miłej lektury.

październik 3, 2009

Michał Piotrowski Sun Certified Java Programmer

Zaszufladkowany do: Life, SCJP — chlebik @ 9:01 am

W końcu po prawie miesiącu od zdania dostałem fajną przesyłkę od firmy Sun. A w niej pierwszy dyplom do zawieszenia na moim ‘Wall of Fame’.

Certyfikaty SUNa mają to do siebie, że dostaje się kilka bajerków po ich zdaniu. Pierwszy z nich to fajna wizytówka w formie karty kredytowej (twardy plastik). Dzięki temu (o ile w ogóle będzie potrzeba) nie ma konieczności targania ze sobą certyfikatu. Zaś sam dyplomik wygląda tak:

mojSCJP

Trochę krzywo się zeskanował, ale przyczyną tego problemu jest… poczta. Otóż dostałem przesyłkę od SUNa zwykłym listem przysłanym z zagranicy (Malmo, 16 września, odebrałem wczoraj zatem trochę im zeszło). Wszystko fajnie, tylko czemu koperta była z gatunku ‘tekturowych’, bez jakiegokolwiek zabezpieczenia w srodku! I właśnie dlatego otrzymana przesyłka w kilku miejsach wygląda jak z gardła wyciągnięta (zakładam, że to wina wilgoci). Nierówna faktura objawia się niemożnością poprawnego zeskanowania niestety. Mam przynajmniej nadzieję, że kiedy włożę ten dyplom do antyramy to się sprasuje i będzie wyglądał cokolwiek lepiej. Pozostaje tylko mieć nadzieję, że kolejne certyfiakty zostaną dostarczone w trochę lepszym stanie.

październik 1, 2009

Do kopania łopata, zaś do Javy…

Zaszufladkowany do: Recenzje — chlebik @ 9:58 pm

javapn No właśnie co do Javy? Ostatnio jakoś cicho na blogu, gdyż przerabiam wydaną w czerwcu książkę, o której już na blogu pisałem – “Java. Praktyczne narzędzia”.

Rzecz jest niebagatelna – prawie 900 stron wiedzy, tym cenniejszej, iż z definicji znalezienie konkretnej literatury dotyczącej poruszanych tematów jest dość trudne. Dla kogo jest ta książka? Zasadniczo dla każdego – początkujący w ogóle zobaczą o co chodzi, zaawansowani ugruntują wiedzę, a i czasem znajdą coś, o czym nie mieli pojęcia. Takie są moje odczucia póki co po lekturze rozdziałów dotyczących Anta i Mavena, a także szybkim przejrzeniu tematu związanego z Subversion.

Twórcy książki skupili się na narzędziach właśnie – nie na omówieniu kolejnej biblioteki służącej do niewiadomoczego. Takiej książki jeszcze na rodzimym rynku nie było i trzeba przyznać,  że tym razem Helion się naprawdę postarał.

Całość jest zorganizowana w działy, których to tematyka prezentuje się następująco:

  • narzędzia do budowy projektów – nazwa może mało precyzyjna, ale jeśli napiszę, że mowa tutaj o Ancie i Mavenie to każdy zrozumie o co chodzi.
  • systemy kontroli wersji – tutaj póki co mam zastrzeżenia. O ile jeszcze jestem w stanie zrozumieć wybór SVNa, o tyle omawianie CVS mija się z celem. Nie lepiej było poruszyć coś bardziej nowoczesnego – GITa, Mercuriala czy Bazarek?
  • Continous Integration – temat mi również ostatnio bliski, gdyż w pracy zaczęliśmy używać od pewnego czasu PHPUnderControl. Rzecz na początku wkurzająca, z czasem staje się wręcz naturalnym elementem tworzenia aplikacji. Autorzy rzecz jasna koncentrują się na Javie – Continuum, Openfire, Hudson i CruiseControl.
  • Testy – wspominałem, że może to narzędzia, a nie konkretne biblioteki, no ale testy są w sumie narzędziem wspomagającym programistów, a nie kolejną rzeczą do doklepania. O testach sporo się pisze, gdyż mowa zarówno o testach jednostkowych (JUnit, TestNG), jak i integracyjnych oraz wydajnościowych. Duuużo stron do przeczytania.
  • Mierzenie poziomu jakości aplikacji – to temat cokolwiek drażliwy dla programistów. Co robić by kod był ładniejszy? Albo miał mniej błędów? Tutaj można sobie o tym poczytać.
  • Narzędzia do zgłaszania błędów – każdy kto uważa, że pisanie komentarzy do TODO w BaseCampie nie jest skuteczną metodą pracy z błędami z pewnością powinien zajrzeć – Trac i Bugzilla to narzędzia omawiane w tej sekcji.
  • Dokumentacja – wszystkim jest potrzebna, wszyscy chcieliby aby istniała, nikt za to nie chce jej tworzyć. Tak, tak, to o dokumentacji mowa. Okazuje się, że w tej syzyfowej pracy nie jesteśmy skazani na porażkę!

Na koniec tego szybkiego przedstawienia należy jednakże wspomnieć o dwóch felerach. Pierwszy to cena – choć niestety za wiedzę trzeba płacić, a tutaj wiedzy mamy całkiem sporo. Drugi zarzut to momentami dość poważne niedopatrzenia w kodzie prezentowanych przykładów. Zdarza się tak, że mowa o jakimś nowym ficzerze, zaś w prezentowanym listingu owego ficzera ani widu, ani słychu. Może to i metoda wspomagania zapamiętywania, jednakże w porządnie wydanej książce nie powinny takie rzeczy mieć miejsca. Mimo to – warto!

wrzesień 23, 2009

I po próżności – wyniki konkursu

Zaszufladkowany do: Blog, Life — chlebik @ 9:32 am

Czas leci, a ja jakoś nie zauważyłem, iż konkurs w którym mój blog brał udział zakończył się ponad tydzień temu, a ja zapomniałem o tym napisać.

Jak widać na stronie konkursu zająłem drugie miejsce. Bardzo mi z tej okazji miło i w tym miejscu chciałbym podziękować wszystkim głosującym. Dobrze jest mieć poczucie, iż to co się robi znajduje uznanie w oczach innych. Jeszcze raz Wszystkim dziękuję.

Starsze wpisy »

Blog na WordPress.com.