Stronnice Chlebika – Newbie Java Blog

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

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.

wrzesień 18, 2009

Strony błędów w JSF i co z tego wynikło

Zaszufladkowany do: JavaServer Faces, ProgramBash — chlebik @ 11:38 pm

Jak pisałem w poprzednim wpisie – na poważnie rozpocząłem deployment aplikacji napisanej w JSF na serwerze hostingowym. Siłą rzeczy powiedzenia programistów “u mnie działa” nie wzięły się znikąd – fakt – u mnie działa, na serwerze już jednak niekoniecznie.

Ostatnio link do mojego bloga pojawia się w masie miejsc. Wzrost ilości odwiedzin jest łatwo widoczny – stąd też nie wszyscy mogą być świadomi, iż do tej pory nie miałem większych problemów z hostowaniem swojej aplikacji napisanej w GrailsHowToJava. Jednakże dorzucenie na serwer kolejnego programistycznego projektu – ProgramBash zaowocowało pewnymi komplikacjami, które zajęły mnie przez ostatnie dni.

Pierwsza rzecz warta wzmianki to fakt, iż oszalał mi NetBeans. Moja sprawdzona wersja 6.5.1 zaczęła w pewnym momencie (pomimo braku zmian w kodzie) wyrzucać przy deploymencie na lokalnym Tomcacie wyjątek o niemożliwości dostania się do elementów layoutu w katalogu WEB-INF i wpadała w kaskadę wyjątków, która skutecznie zabierała 100% procesora i trzeba było zabijać proces IDE i serwera. Z konieczności zatem zdecydowałem się na zmiany, które zaowocowały instalacją wersji 6.7.1 – na razie działa stabilnie, oby tak dalej.

Pisałem również wcześniej, iż w tym projekcie użyję Postgresa. Jednakże zanim doczekam się go na serwerze pewnie jeszcze trochę minie – zatem z konieczności development toczy się na starym dobrym MySQL. I nie byłoby w tym nic strasznego gdyby nie fakt, iż o ile HowToJava pięknie sobie z bazą radzi, o tyle ProgramBash zaczął “wysiadać”. Konkretnie aplikacja wyrzucała wyjątki mówiące o zakończeniu połączenia.

Wyruszyłem na mały research. Uzbrojony w nowe IDE mogłem zabrać się za rozwiązywanie problemu, który dość często uniemożliwiał spojrzenie na aplikację, gdyż dostawało się komunikat błędu. Zapuściłem się w otchłań internetu z wujkiem Google i oto co się okazało – Hibernate standardowo musi zostać stuningowany, aby nadawał się do celów produkcyjnych (szkoda tylko, że moja mądra książka o Hibernate jakoś o tym nie wspomina). Oto urywek z dokumentacji:

Hibernate’s own connection pooling algorithm is, however, quite rudimentary. It is intended to help you get started and is not intended for use in a production system, or even for performance testing. You should use a third party pool for best performance and stability.

No dobra, zatem trzeba pomyśleć co z tym fantem zrobić. Ano okazuje się, że należy zainteresować się dostarczanym wraz z Hibernate pakietem/biblioteką o nazwie C3P0. Nadpisuje ona domyślne ustawienia connection pools i umożliwia zabawę na bardziej wymyślnym placu zabaw. Zatem do naszego pliku hibernate.cfg.xml należy dorzucić następujący fragment kodu:

    <property name="hibernate.c3p0.min_size">5</property>
    <property name="hibernate.c3p0.max_size">20</property>
    <property name="hibernate.c3p0.timeout">300</property>
    <property name="hibernate.c3p0.max_statements">50</property>
    <property name="hibernate.c3p0.idle_test_period">3000</property>
    <property name="current_session_context_class">thread</property>

I w ten sposób problemy z niestabilnością połączenia rozwiązują się w mgnieniu oka. Hibernate ponoć tę bibliotekę posiada – jednakże w wersji dostarczanej przez NetBeans jakoś jej nie podpięto. Zatem trzeba dodać kolejnego JARa do naszych bibliotek w aplikacji.

Deployment i działa. Póki co :) Jednakże wraz z wyrzucaniem błędów w połączeniu naszła mnie myśl o zmianie wyświetlanej strony błędów. Wszak wyrzucenie StackTrace użytkownikowi końcowemu nie jest raczej zachowaniem pożądanym. Rzecz jasna wziąłem się za poprawę tego stanu rzeczy.

Sprawa wydawała się prosta. Obsługę błędów w JSF można rozwiązać na dwa sposoby. Albo dodać stosowne dyrektywy do strony widoku (dobre chyba w rozwiązaniach typu HelloWorld), albo też zadeklarować rzecz w deskryptorze wdrożenia. Oto jak wygląda plik web.xml po utworzeniu projektu w JSF:

 <error-page>
        <exception-type>javax.faces.FacesException</exception-type>
         <location>/servlet/ExceptionHandlerServlet</location>
    </error-page>

    <error-page>
        <exception-type>javax.servlet.ServletException</exception-type>
        <location>/servlet/ExceptionHandlerServlet</location>
    </error-page>
    <error-page>
        <exception-type>java.io.IOException</exception-type>
        <location>/servlet/ExceptionHandlerServlet</location>
    </error-page>

    <error-page>
        <exception-type>com.sun.rave.web.ui.appbase.ApplicationException</exception-type>
        <location>/servlet/ExceptionHandlerServlet</location>
    </error-page>

    <servlet-mapping>
        <servlet-name>ExceptionHandlerServlet</servlet-name>
        <url-pattern>/error/ExceptionHandler</url-pattern>
    </servlet-mapping>

   <servlet>
        <servlet-name>ExceptionHandlerServlet</servlet-name>
        <servlet-class>com.sun.errorhandler.ExceptionHandler</servlet-class>
              <init-param>
            <param-name>errorHost</param-name>
            <param-value>localhost</param-value>
        </init-param>
        <init-param>
            <param-name>errorPort</param-name>
            <param-value>24444</param-value>
        </init-param>
    </servlet>

I w tym momencie wszystkie błędy są obsługiwane przez standardowe widoki JSF. By zmienić ten stan rzeczy najprościej jest po prostu utworzyć stronę widoku (HTML, JSP) i podlinkować do niej obsługę błędów poprzez odpowiednie nadanie wartości parametru location. Powinno to wyglądać zatem tak:

<error-page>
        <exception-type>com.sun.rave.web.ui.appbase.ApplicationException</exception-type>
        <location>/error.html</location>
    </error-page>

I w ten sposób wyszczególniony wyjątek będzie obsługiwany przez naszą stronę. Najlepiej jest zdefiniować jedną stronę błędów, a pod nią podpiąć wszystkie błędy. Najlepiej zatem tak:

<error-page>
        <exception-type>java.lang.Throwable</exception-type>
        <location>/error.html</location>
    </error-page>

I życie od razu staje się przyjemniejsze. Istnieje też druga wersja – można wyszczególnić kod błędu (konkretnie kodów HTTP). Czyli by ładnie obsłużyć stronę 404 (nie znaleziono zasobu) można zrobić coś takiego:

<error-page>
        <error-code>404</error-code>
        <location>/error404.html</location>
    </error-page>

I w taki oto sposób ProgramBash przybliżył się do działającej aplikacji. Mam nadzieję, że w przyszłym wpisie zajmę się już czymś bardziej konkretnym – wstępnie myślę o rejestracji i logowaniu. Na otarcie łez można rzucić okiem na użycie RichFaces klikając na link O AUTORZE.

wrzesień 14, 2009

Deploymentów ciąg dalszy

Zaszufladkowany do: Blog, HowToJava, ProgramBash — chlebik @ 10:32 pm

Postanowiłem uporządkować trochę wszystkie tematy związane z napisanymi do tej pory aplikacjami oraz hostingiem. Istniejąca do tej pory domena chlebik.pl wskazywała na ostatni skończony projekt czyli HowToJava. Dziś uległo to zmianie.

Pod wyżej wymienionym adresem można znaleźć skromną stronkę z linkami do istniejących projektów (w tym bloga oraz klienta do gry w arkę). Dzięki temu domena służy zbieraniu wszystkiego w jednym miejscu, zaś konkretne aplikacje (nawet działające) można znaleźć pod stosownymi adresami. Od tej pory wszystkie zmiany w pisanych przeze mnie aplikacjach będą automatycznie umieszczane na serwerze wraz z kolejnymi wpisami. Od tej pory wystarczy pamiętać jeden adres – chlebik.pl

wrzesień 13, 2009

Program Bash również w sieci

Zaszufladkowany do: JavaServer Faces, ProgramBash — chlebik @ 10:17 pm

Zgodnie z obietnicą z poprzedniego postu wracam do programowania. Ostatni wpis dotyczący ProgramBash miał miejsce bodajże 5 sierpnia. Minęło całkiem sporo czasu zatem wypada coś z tym tematem zrobić.

Dziś zaś nie zamierzałem rozpoczynać tworzenia nowej funkcjonalności, ale za to postanowiłem wrzucić to co do tej pory istnieje na swój serwer hostingowy. Zakładam, że o wiele milej jest zobaczyć coś w działaniu, niż tylko podglądać screeny. Proces deplyomenu był całkiem miły i przebiegł bez zakłóceń – stworzenie pliku WAR, następnie eksport na serwer, dorzucenie kontekstu dla Jetty i poszło. Zasadniczo problemów nie było – poza jednym. Domyślne formatowanie taga (czy tagu?) <f:convertDateTime /> leci po ustawionym Locale. Jednakże wpis na ten temat jest (zakładam) odczytywany z systemu, albo też NetBeans przy budowaniu projektu i uruchamianiu go u mnie lokalnie podpina moje ustawienia lokalne (czyli polskie). Zaś na serwerze takowe nie chciały zaskoczyć i nagle daty przy newsach zaczęły wyświetlać się po angielsku. Pomogło umieszczenie w pliku faces-config.xml takiego oto kodu:

 <application>
    <locale-config>
      <default-locale>pl</default-locale>
    </locale-config>
  </application>

I poszło bez problemu. Obecnie aplikację można obejrzeć sobie pod tym adresem. Ostatnio serwery, na których trzymam swoje “dzieła” objawiają się dziwnym zachowaniem zatem jeśli coś nie działa to zapraszam do odwiedzin za jakiś czas. Cóż, może w końcu się wykosztuję na jakiś full-professional serwis, no choć te kilka stów rocznie można przeznaczyć zawsze na coś innego.

sierpień 5, 2009

ProgramBash – Hibernate wkracza do gry

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

Jak pisałem w poprzednim wpisie na podpięcie bazy danych miał przyjść czas. No i ten czas właśnie nadszedł. W dzisiejszym wpisie zajmiemy się podpięciem do naszej aplikacji ORMa Hibernate i spróbujemy go zmusić do pracy z PostgreSQL. A kiedy już zmusimy to i może coś ładnego da się z tego wykrzesać. Zaczynamy.

Pierwszą rzecz, a mianowicie zaimportowane biblioteki do obsługi Hibernate mamy już za sobą. Podczas tworzenia projektu zaznaczyłem opcję mówiącą o imporcie tej jakże przydatnej biblioteki. Domyślnie ustawiłem dialekt na MySQL, jednakże po pobieżnym researchu okazało się, że u mnie na hostingu da się bez problemu zainstalować Postgresa w związku z tym zaczniemy od zmiany pliku konfiguracyjnego – wszak i lokalnie będę pracował z tym systemem bazodanowym.

Obslugiwany dialekt Hibernate znajdziemy spokojnie w źródłach tejże biblioteki, jednakże sterownik dla Postgresa już niekoniecznie. Zatem wypada ściągnąć najnowszą wersję sterownika i dodać do bibliotek w naszym projekcie (PPM na Libraries -> Add i wskazujemy plik ). Ja wrzuciłem swój do katalogu \WEB-INF\lib wraz z kilkoma innymi bibliotekami.
Edytujemy zatem plik hibernate.cfg.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.connection.url">jdbc:postgresql://localhost/bash:5432</property>
<property name="hibernate.connection.username">chlebik</property>
<property name="hibernate.connection.password">chlebik</property>

     <mapping resource="/hibernate/mapping/News.hbm.xml"/>

  </session-factory>
</hibernate-configuration>

Jak widać obok standardowych zapisów mówiących o dialekcie i takich tam dodałem już z góry oddzielny zapis o pliku odwzorowania. Pakiet, który będzie zawierał pliki odwzorowań dla Hibernate (jest to najzwyklejszy pakiet utworzony w katalogu ze wszystkimi innymi pakietami) będzie nazywał się com.wordpress.chlebik.mappings. Na pierwszy ogień zaimplementujmy coś prostego – choćby listę newsów pojawiających się zaraz po wejściu do serwisu. Plik News.hbm.cfg prezentuje się następująco:

<hibernate-mapping package="hibernate.mappings">
 <class name="com.wordpress.chlebik.News" table="news">
 <id name="id" type="long" column="id">
 <generator />
 </id>
<property name="adddate" column="adddate" type="integer" />
<property name="header" column="header" type="string" />
<property name="content" column="content" type="string" />
 <class>
</hibernate-mapping>

Jak widać nie jest on specjalnie skomplikowany. Relacji nie ma, raptem 4 pola, wszystko w miarę proste i oczywiste. Jest to odwzorowanie zwykłego POJO, którego zresztą trzeba stworzyć.

package com.wordpress.chlebik;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * Klasa, ktora ma za zadanie byc beanem wystepujacym jako news na portalu
 *
 * @author Michal Piotrowski
 */
@Entity
@Table(name="news")
public class News {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    private String header;
    private String content;
    private Integer adddate;

    public News() {
    }

    public News( String header, String content, Integer adddate ) {
        this.header = header;
        this.content = content;
        this.adddate = adddate;
    }

    // Gettery
    public String getHeader()
    {
        return header;
    }

    public String getContent()
    {
        return content;
    }

    //  To jest konieczne by moc formatowac date za pomoca znacznikow JSF
    public Date getAdddate()
    {
        Date dateDateObject = new Date();
        long dateInMilis = ((long)adddate)*1000;
        dateDateObject.setTime(dateInMilis);
        return dateDateObject;
    }

    protected Long getId()
    {
        return id;
    }

     // Settery - bardziej dla poprawnosci niz potrzeby
    public void setHeader( String header )
    {
        this.header = header;
    }

    public void setContent( String content )
    {
        this.content = content;
    }

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

    protected void setId( Long id )
    {
        this.id = id;
    }
}

Mamy odwzorowanie, mamy POJO, co teraz? Normalnie wypadałoby zrobić dwie rzeczy – zapewnić sobie jeszcze jedną klasę, która zajęła by się tylko dostępem do danych. Czyli np. wyciąganiem obiektów, ich zapisywaniem i takimi tam. W przypadku zwykłych newsów, które na razie będą dodawane bezpośrednio przez bazę (tworzenie panelu administracyjnego dla mojej skromnej edukacyjnej aplikacji uważam za pozbawione sensu), nie ma absolutnie potrzeby rozgraniczanie logiki biznesowej na kolejne warstwy. Przykład jest póki co banalny, myślę że nie popełnię większego naduzycia jeśli po prostu wrzucę metodę wyciągającą ostatnie newsy w komponencie zarządzanym i na tym poprzestaniemy.

Teraz sprawa druga – dostęp do bazy danych. Jak piszą autorzy mojej książki o Hibernate obiekt, który zawiaduje połączeniami do bazy danych i całym tym jarmarkiem to SessionFactory. Jest to jednakże obiekt “ciężki” (heavyweight object), dlatego najlepiej mieć go raptem jeden na całą aplikację. Co zatem trzeba zrobić? Ano jak piszą mądrzy ludzie najlepiej zaimplementować klasę, która będzie trzymała takowy “ciężki” obiekt w formie zmiennej statycznej i w ten sposób po uruchomieniu aplikacji będziemy mogli wyciągać tylko obiekty sesji z istniejącej już fabryki.

Robimy to następująco. Wybieramy sobie pakiet, w którym chcemy takowy obiekt umieścić i prawy przycisk myszy -> New -> Other -> Hibernate -> Hibernate Util. Dzięki temu mamy od razu stworzoną klasę, która wszystko pięknie nam obsłuży. U mnie wygenerowany plik wygląda tak:


package com.wordpress.chlebik;

import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.SessionFactory;

/**
 * Hibernate Utility class with a convenient method to get Session Factory object.
 *
 * @author chlebik
 */
public class ProgramBashUtil {
    private static final SessionFactory sessionFactory;

    static {
        try {
            // Create the SessionFactory from standard (hibernate.cfg.xml)
            // config file.
            sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            // Log the exception.
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

Uff. Mamy chyba wszystko. Pozostaje nam podpiąć pod główną stronę pięknego managed-beana i jesteśmy prawie w domu. Wpierw XML (plik faces-config.xml), potem kod Javy:

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

No i teraz kod Javy. Nasze ziarenko będzie posiadało tylko jedną metodę – getNews, co pozwoli wylistować wszystkie newsy z bazy. Listing zatem przedstawia się dość prosto:

package com.wordpress.chlebik;

import java.io.Serializable;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

public class NewsBean implements Serializable {

    public NewsBean() {
    }

    public List<News> getNews()
    {
        SessionFactory sessionFactory = com.wordpress.chlebik.ProgramBashUtil.getSessionFactory();
        Session session = sessionFactory.openSession();

        List<News> newsy = session.createQuery("from News order by adddate DESC").list();
        return newsy;
    }
}

Ano i tyle. Od tej pory wystarczy w plikach JSP odwołać się do zapisu #{newsBean.news} i mamy piękną listę newsów. Ja wrzuciłem listowanie newsów na stronę główną w taki oto sposób:

<rich:dataList var="news" value="#{newsBean.news}" rowClasses="newsElement">

  <div>
      <div class="newsHeader"><h:outputText value="#{news.header}"/></div>
      <div class="newsDate">
         <h:outputText value="#{news.adddate}">
             <f:convertDateTime type="date" dateStyle="full" />
        </h:outputText>
      </div>
      <div class="newsContent"><h:outputText value="#{news.content}" /></div>

  </div>

</rich:dataList>

Rzecz jasna klasy CSSa również ładnie podpiąłem i stąd ładny widok. Nieprawda?

glownaHibernateProgramBash

lipiec 25, 2009

ProgramBash czyli o programowaniu na wesoło

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

Przed urlopem umieściłem informację o nowym projekcie, który zamierzam zrealizować w ramach nauki Javy. Padło na JSF, Hibernate i RichFaces. Powstała kategoria ProgramBash, która jest jednocześnie nazwą aplikacji. A o co w niej konkretnie chodzi? Wszyscy znają portale takie jak http://bash.org.pl, czy http://demotywatory.pl – ja z kolei wpadłem na pomysł by użyć podobnego konceptu do publikacji śmiesznych/ciekawych fragmentów kodu, albo też sytuacji z życia zespołów programistycznych. Nie jest to zbyt skomplikowane, ale na pewno jest dobrym pomysłem na “edukacyjną” aplikację.

Oczywiście jak zawsze trzeba wyjść od wyglądu. Ja tak jakoś mam, że w projektach, które tworzę na bloga lubię pracować od razu na gotowym szablonie graficznym – od samego początku mogę publikować ładne screeny i mam większe pojęcie o tym jak ostatecznie będzie wyglądać aplikacja. Tym razem wybrałem ciekawy layout, który pobrałem ze strony free-css-templates.com o nazwie redbusiness. Mając zaś gotowy layout pora na przemyślenie ogólnej koncepcji serwisu. Generalnie na pewno będzie możliwość dodawania wpisów (odkrywcze), ich kategoryzacji, przeglądania według różnych kryteriów oraz oczywiście oceniania. Zasadniczo nie wyjdzie to w jakiś znaczący sposób poza CRUDa, choć może zerknę edukacyjnie w stronę webservice. Dla własnego rozwoju rzecz jasna.

Pierwotnie celem tego wpisu było stworzenie krok po kroku funkcjonującej strony głównej, skonfigurowanie Hibernate w stopniu umożliwiającym korzystanie z danych zawartych w bazie, a także zrobienie czegoś ładnego z RichFaces. Jednakże jestem przeciwny publikacji kolubryn na kilkadziesiąt stron, zatem ten wpis na razie zostanie poświęcony tylko ogarnięciu widoku i layoutu, a także zapoznanie się z pakietem Apache Tiles.

Operować zaczniemy na przykładzie, który już wcześniej stworzyłem. Jak już pisałem wcześniej JSF ma to do siebie, że w czystej postaci niewiele jest w stanie zdziałać. Dotyczy to również domyślnych metod renderowania widoku i ogarnięcia czegoś tak prozaicznego jak layout. W mojej książce mądrzy ludzie napisali, iż mamy w tej materii do wyboru dwie drogi. Pierwsza z nich zakłada tworzenie kodu w formie old-JSP, a mi brzydko się ona kojarzy z instrukcją require_once z PHP. W sumie i nic dziwnego, gdyż metoda ta zakłada stworzenie oddzielnych widoków – np. górnej belki, menu, czy stopki. Zaś następnie należy takowe importować (poprzez używanie znacznika c:import z biblioteki JSTL). Nie jest to dobre rozwiązanie.

Drugie podejście wiąże się z użyciem pakietu Apache Tiles. Cały ten pakiet służy właśnie temu, aby przede wszystkim za pomocą tzw. “kafelków” (stąd nazwa), zarządzać widokami w aplikacji javowej. Należy odwiedzić oficjalną stronę pakietu i ściągnąć najnowszą wersję – 2.3.1. Następnie wypakować wszystkie pliki JAR z archiwum (z głównego katalogu oraz podkatalogu /lib) i wrzucić do katalogu WEB-INF/lib w naszej aplikacji. Tak twierdzi dokumentacja. Następnie trzeba poinformować naszą aplikację (poprzez plik web.xml) o serwlecie, który będzie nam potrzebny. Do tego pliku dopisujemy taki kod:

<servlet>
<servlet-name>tiles</servlet-name>
<servlet-class>org.apache.tiles.web.startup.TilesServlet</servlet-class>
<init-param>
<param-name>
 org.apache.tiles.definition.DefinitionsFactory.DEFINITIONS_CONFIG
</param-name>
<param-value>
 /WEB-INF/tiles-defs.xml
</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>

Run aplikacji… ZONK. Oczywiście krzyk, że nie potrafi odnaleźć takiej klasy jak org.apache.tiles.web.startup.TilesServlet. PPM na elemencie library w projekcie i dodanie ww. plików JAR załatwiło sprawę. Apache Tiles wstępnie skonfigurowane.

Zabawa z konfiguracją kafli dopiero się zaczyna. Pomysł generalnie jest taki – tworzymy na razie jeden plik z layoutem przeznaczony dla użytkowników niezalogowanych w serwisie. Taki plik layoutu będzie nam renderował górne menu, główną zawartość konkretnej strony oraz stopkę. Nad wyhaczeniem co z czym spędziłem chyba łącznie 3 godziny. Moja książka do JSF jedno, dokumentacja co innego, a praktyka pokazała jeszcze co innego. Masakra. Generalnie jak widze najlepszym przyjacielem jest NetBeans, który podpowiada co w danej sytuacji/tagu można wpisać – podpowiedzi z definicji działają. Koniec gadania – oto co trzeba zrobić:

  • XML – czyli musimy dopisać odpowiednie zapisy konfiguracyjne w pliku WEB-INF/tiles-defs.xml
  • wrzucić CSS oraz obrazki do niego
  • zaimplementować wygląd kafelka w formie pliku(ów) JSP
  • wrzucić wygląd do konkretnej strony JSP, którą chcemy wyświetlić

Jak wygląda layout już wiadomo. Plik konfiguracyjny powinien zatem wyglądać mniej więcej tak:

<tiles-definitions>
<definition name="guestLayout" template="/WEB-INF/layout/guestLayout.jsp">
<put-attribute name="upperMenu" value="/WEB-INF/layout/upperMenuTemplate.jsp" />
<put-attribute name="content" />
<put-attribute name="footer" value="/WEB-INF/layout/footerTemplate.jsp" />
</definition>
</tiles-definitions>

W tym kroku tworzymy definicję – czyli określamy pewien abstrakt widoku, który otrzymuje stosowną nazwę, wiążemy go z konkretnym widokiem istniejącym gdzieś w formie pliku JSP, a także nadajemy mu atrybuty, którym również przypisujemy nazwy i wartości (kolejne odwołania do plików JSP). Uwaga! Nalezy zwrócić uwagę, że wszystkie ścieżki podajemy bezwzględnie (zaczynamy je znakiem ‘/’), a korzeniem jest folder web. Może pojawić się pytanie – dlaczego elementy layoutu umieszczamy w katalogu WEB-INF? To dość proste – by nie można było się do nich dostać pojedynczo poprzez przeglądarkę.

Stworzyłem plik guestLayout.jsp. Pytanie kolejne! Jakie ścieżki wpisać w sekcji HEAD naszej strony layoutu? Choćby by dobrać się do plików CSS lub JSa? Ano i tutaj mała niespodzianka – podajemy ścieżkę względem katalogu web, jednakże nie traktując go jako korzenia. Oto jak to wygląda u mnie:

	<link rel="stylesheet" type="text/css" href="css/style.css" media="screen" />

Rzecz jasna utworzyłem sobie folder css, aby nie wprowadzać niepotrzebnego bałaganu. CSS na pewno mamy już podpięty, ale co w takim razie zrobić z grafiką w projekcie? Obrazki z layoutu wrzuciłem również do katalogu web do utworzonego specjalnie w tym celu podkatalogu images. By jednakże były one przez nasz plik CSSa widoczne, musimy ich adresowanie z poziomu reguł CSSa potraktować relatywnie. Czyli wygląda to choćby tak:

#wrap #menu li { line-height: 26px; background: #F6F6F6 url(../images/li-line.gif) no-repeat bottom left;}

Uporawszy się z tymi zagadnieniami pora spojrzeć na plik guestLayout.jsp. U mnie wygląda on tak:

<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>

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

<%@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"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<f:view>
<html>
    <head>
        <title>ProgramBash</title>
        <meta http-equiv="Content-Language" content="Polish" />
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <meta name="author" content="Michał 'Chlebik' Piotrowski" />
        <meta name="Robots" content="index,follow" />
        <link rel="stylesheet" type="text/css" href="css/style.css" media="screen" />
    </head>

    <body>

        <tiles:insertAttribute name="upperMenu" />

        <div class="content">

            <div id="main">

                <div id="left_side">

                    <div class="rs">
                         <tiles:insertAttribute name="content" />
                    </div>
                </div>

                <tiles:insertAttribute name="footer" />

            </div>
    </body>
</html>
</f:view>

Nie ma tu niczego odkrywczego z wyjątkiem elementów <tiles: >. Ano służą one w tym miejscu do “wrzucenia” pewnych konkretnych atrybutów – czyli w naszym przypadku po prostu kolejnych widoków, które znajdują się w plikach JSP. Atrybuty o nazwach upperMenu oraz footer zostały przyporządkowane do plików JSP w deklaracji definicji widoku w pliku tiles-defs.xml. Jednakże pozostawiliśmy tam wolne miejsce dla atrybutu content, tzn. nie przypisaliśmy mu żadnego widoku. I tak miało być! Albowiem ten atrybut będzie reprezentował bieżący widok, który jest różny dla każdego requestu (no może nie każdego, większości).

Docelowo operowałem na pliku welcomeJSF.jsp, który leży sobie od samego początku w katalogu web. Wystarczyło przeredagować go na taką formę:

<%@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" %>
<tiles:insertDefinition name="guestLayout" flush="true">
<tiles:putAttribute name="content" value="/glowna/content.jsp" />
</tiles:insertDefinition>

I przy posiadaniu takiej struktury plików:

strukturaProjektuJSF

Ostatecznie mym oczom ukazał się taki jakże miły każdemu sercu widok:

glownaProgramBash

To tyle na dziś. Wygląda to bardzo łatwo i skromnie, ale przyznaję, że trochę czasu nad rozkminianiem tego spędziłem. Zadziwiająco mało momentami Google wie na temat JSF i ewentualnych jego bugów czy współpracy z innymi technologiami. Oby się to zmieniło.

czerwiec 13, 2009

JSF i RichFaces – pierwsze starcie

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

Za namową Darka Ludery postanowiłem troszeczkę jeszcze pogrzebać przy samym wyborze JSF. To, że jest to framework wręcz stworzony do rozszerzania o inne funkcjonalności już dowiedziałem się wcześniej. Zresztą w książce o JSF piszą o Apache Tiles, Strutsie i trochę o AJAXie. Darek w komentarzu do poprzedniego mojego wpisu zasugerował RichFaces. Zajrzałem, popatrzyłem i jest fajnie – JQuery UI dla Javy, ale tak na oko 6,5 raza lepszy.

No ale dość gadania. Wypadałoby zabrać się do jakieś roboty. Próbowałem stworzyć projekt na swoim starym dobrym NetBeansie w wersji 6.5, ale cosik mi się popieprzyło (nagle uruchomienie Tomcata wywalało błąd, że kompilator nie może odnaleźć klasy Object, wtf?). Przeinstalowałem zatem temat i na pokładzie mam ślicznego NetBeansa w wersji 6.5.1, GlassFisha w wersji jeszcze 2 (rozwojowa wersja 3 mnie póki co nie interesuje), no i przeinstalowałem Tomcata. Nie wiem czy zapamiętano wszystkie pluginy, które wcześniej miałem poinstalowane czy co, ale kwestia jest taka, że po upgradzie dalej sypało mi błędami. Jednakże może wpierw proces tworzenia projektu:

Tworzymy nowy projekt. File->New Project->Java Web->Web Application.

jsf_new_step1

Wybieramy nazwę dla projektu, lokalizację i takie tam:

jsf_new_step2

Środowisko uruchomieniowe:

jsf_new_step3

No i na końcu dorzucamy sobie biblioteki, które chcemy wykorzystać. Dla przypomnienia – Hibernate, JSF, RichFaces.

jsf_new_step4

Klikając na kolejne elementy listy na dole pojawiają się opcje konfiguracyjne. Ja tam ustawiłem wersję dla JSF (1.2), a także wybrałem połączenie MySQL. Zainstalowałem sobie lokalnie PostgreSQL. Jeśli na moim hostingu dałoby się toto również zainstalować, wówczas cała aplikacja będzie powstawała z tym systemem bazodanowym. Zawsze coś innego niż do tej pory (z PostgreSQL pracowałem ze dwa miesiące może w poprzedniej firmie). Pozostaje też pytanie o mój hosting. Podobnie jak z HowToJava byłoby dobrze móc pokazywać efekty swojej pracy szerokiej publice – jeśli da radę zainstalować na serwerze PostgreSQL to będzie w ogóle miło.

Na sam koniec wypada pokazać jak wygląda nasz nowo utworzony projekt:

jsf_new_step5

Wygląda niby ciekawie, ale jak już wspomniałem wcześniej – takie coś się nie uruchomi! (przynajmniej u mnie nie chciało). Przeorałem net i w końcu postanowiłem iść metodą drobnych kroczków.
Sypała błędami deklaracja taglibów w pliku JSP:

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

Po usunięciu z kodu wszystko działa ślicznie pięknie, idzie deploy, Tomcat odpala się aż miło. Generalnie na necie znalazłem opis podobnego przypadku, ale kolesiowi wyrzucało problemy, gdyż dodatkowo miał wrzucony do WEB-INF/lib plik jar z biblioteką ajax4jsf. Ja nie miałem i w myśl tego tutoriala (oficjalnego) mieć nie powinienem. Jedyną zadeklarowaną biblioteką znaczników powinna być:

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

Tylko to dotyczy tylko i wyłącznie RichFaces. No a gdzie AJAX mój kochany? Postanowiłem nie dać za wygraną i opdrążyć temat głębiej. Dlaczego bowiem domyślne odpalenie projektu skutkuje błędami? Aż taki bug? Rozumiem, że wsparcie dla RichFaces jest pluginem, no ale bez przesady. Wspomniana biblioteka ajax4jsf została zintegrowana z RichFaces już jakiś czas temu. Dziwne toto.

Zrobiłem tak. Usunąłem z libraries wpisy dotyczące RichFaces. Po czym ściągnąłem najnowszą wersję tego frameworka, rozpakowałem 3 jary do WEB-INF/lib i dodałem je do projektu. Run i… DZIAŁA! Jednakże w logu uruchomienia Tomcata dalej znajduję takie coś:

2009-06-13 01:53:06 org.apache.catalina.loader.WebappClassLoader loadClass
INFO: Illegal access: this web application instance has been stopped already.
Could not load java.lang.Object.
The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
java.lang.IllegalStateException

No ja rozumiem, debugging purposes, ale dlaczego wszystko poza tym działa? Wpisuję w okienko tekst, a on się automatycznie wyświetla poniżej (czyli a4j zaskoczył). Totalnie nie mam pojęcia o co w tym chodzi. Obiecywałem sobie, że w Javie będę grzecznie orientował się na wiedzę, a nie osiągnięte cele, a tutaj mam zagwozdkę – cel osiągnięty, wiedzy do końca to nie ma. No ale może mądrzejsi po przeczytaniu tego postu podsuną jakieś rozwiązanie. Na pożegnanie screen z “HelloWorld” konglomeratu JSF+RichFaces

jsf_new_step6

czerwiec 10, 2009

JSF czas zacząć

Zaszufladkowany do: JavaServer Faces, ProgramBash — chlebik @ 11:08 pm

Nawiązując do poprzedniego posta – pomyślałem, przemyślałem i stanęło na nauce JSF. Powodów jest kilka. Po pierwsze kiedy mówimy o frameworkach webowych dla Javy, na samym początku trzeba sobie odpowiedzieć na pytanie: jeśli framework to czego dotyczy? Wydawałoby się, że Javy rzecz jasna! No ale to nie końca tak. W przypadku zastosowań webowych bardziej odpowiednią odpowiedzią byłyby z całą pewnością wzorzec MVC i servlety. To od nich (po niepowodzeniu apletów) możnaby wytyczyć szlak, którym poszła Java i zdobyła rzesze serwerów i programistów :)

Same JSP są technologią tak niskopoziomową, że gdzieś wyczytałem porównanie jej do assemblera. Ciekawe muszę przyznać. Utwierdziło mnie to w przekonaniu, że próba opanowania “czystych” JSP mija się trochę z celem, zaś sam wzorzec MVC (wraz z servletami, które u mnie w Zend Frameworku to po prostu kontrolery) znam i nie uważam bym mógl się czegoś więcej tutaj dowiedzieć. Natomiast JSF obiecuje programowanie na cokolwiek wyższym poziomie, no i do tego jeszcze da się ten framework pointegrować z szeregiem innych rozwiązań (jak choćby Hibernate).

Przyznam też, że po zabawie z Grails odczuwam potrzebę troszeczkę bliższej styczności z kodem. Do tej pory jakoś uniknąłem olbrzymich ilości XMLa w plikach konfiguracyjnych, jakoś też nie spędziłem za dużo czasu na konfiguracji aplikacji, mapowaniu ścieżek i tym podobnych. Pora to nadrobić. Założenie jest takie, aby dość szybko napisać malutką aplikacyjkę, która pokaże co najważniejsze w JSF. Zintegrować to z Hibernate i na końcu zastanowić się co dalej – czy przemielić aplikacyjkę i przepisać część funkcjonalności na inne potencjalne rozwiązania, czy też wystartować z nowym projektem w zupełnie innej technologii? Czas pokaże co z tego wyniknie.

Blog na WordPress.com.