Latest Entries »

Tuesday, December 8, 2009

Application is already deployed on other targets.

Application is already deployed on other targets.
Please use create-application-ref command to create reference to the specified target.

Czasami podczas uruchamiania aplikacji web tworzonej w środowisku NetBeans (a także innych, które automatyzują
proces wdrażania deployingu aplikacji) pojawia się naszym oczom błąd builda. Błąd jest powodowany tym, że serwer aplikacji(Glassfish 2.1) nie pozwala na zastąpienie aktualnej aplikacji, która jest
uruchomiona na serwerze przez nową, którą właśnie uruchomiliśmy(pod warunkiem poprawnej kompilacji).
Żeby pozbyć się błędu i mieć możliwość uruchomienia aplikacji musisz wejść zalogować się jako administrator serwera.
Wchodzimy w zakładkę Services klikamy PPM na Glassfish 2.1 i wybieramy View Admin Console. Gdy naszym oczom ujawi
się strona z konfiguracją serwera musimy przejść do zakładki Applications -> Web Application i wykonać undeploy na naszej
nieszczęśliwej aplikacji :-)

Szczegóły problemu:
http://www.netbeans.org/issues/show_bug.cgi?id=66983

Dzięki za uwagę.

Saturday, November 7, 2009

Niestandardowa konwersja i weryfikacja JavaServer Faces

Po poznaniu podstawowych konwerterów i weryfikatorów JSF-ów możemy przystąpić do trudniejszego zadania.
Jak wiadomo wszystko w Java jest obiektem. Właśnie w ten sposób wszystkie informacje są zapisywane na serwerze. Natomiast informacje, które są wyświetlane w oknie przeglądarki muszą być prezentowane jako napisy, listy i obrazy. Powoduje to potrzebę konwersji tych dwóch rodzajów informacji. Na początku zajmiemy się tworzeniem własnych klas implementujących interfejs javax.faces.convert.Converter, który posiada dwie metody:
public Object getAsObject(FacesContext context, UIComponent component, String value) {...}

public String getAsString(FacesContext context, UIComponent component, Object value) {...}

Pierwsza metoda naszej klasy konwertuje wpisaną wartość tekstową na obiekt dowolnej klasy gdy wysyłamy formularz na serwer, a druga metoda konwertuje wszystkie obiekty na postać łańcuchów i wysyła je do przeglądarki.
Utwórzmy następującą hierarchię klas:
public class ZipCode {
//Klasa reprezentuje kod pocztowy.
private String zip;
public ZipCode(String zip) {
this.zip = zip;
}

public String getZip() {
return zip;
}

public void setZip(String zip) {
this.zip = zip;
}

@Override
public String toString() {
return zip;
}
}
//Klasa imię użytkownika.
public class Name {
private String name;

Name(String value) {
this.name = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
//Klasa reprezentuje obiektowe nazwisko.
public class Surname {
private String surname;
public Surname(String surname) {
this.surname = surname;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
@Override
public String toString() {
return surname;
}
}
//Nasz Bean, który trzyma dane formularza:
public class RegistrationForm2 {
private Name name = new Name("");
private Surname surname = new Surname("");
private ZipCode zipCode = new ZipCode("");
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;

}
public Surname getSurname() {
return surname;
}
public void setSurname(Surname surname) {
this.surname = surname;
}
public ZipCode getZipCode() {
return zipCode;
}
public void setZipCode(ZipCode zipCode) {
this.zipCode = zipCode;
}
}
Teraz aby implementacja JSF wiedziała co zrobić z naszymi obiektami ZipCode,Name,Surname musimy pomóc i stworzyć odpowiednie klasy konwerterów np: ZipCodeConverter, NameConverter i SurnameConverter. Gdy konwersja się nie powiedzie powinniśmy wystosować odpowiedni wyjątek ConverterException().


//---------------------------------
import java.util.Locale;
import java.util.ResourceBundle;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;


/**
*
* @author riaa
*/
public class NameConverter implements Converter{

//Metoda konwertuje łańcuch z przeglądarki na obiekt typu Name.
public Object getAsObject(FacesContext context, UIComponent component, String value) {
//Jeżeli Name jest puste.
if (value.isEmpty())
{
//Pobieramy ustawienia lokalizacji użytkownika przeglądarki.
Locale L = context.getViewRoot().getLocale();
//Tworzymy obiekt, który ładuje klasy przy pomocy metod klasy Class
//a nie standardowo przez konstruktory.
ClassLoader loader = Thread.currentThread().getContextClassLoader();
//Pobieramy obiekt mymessages.properties przy pomocy
//statycznej metody: ResourceBundle.getBundle
ResourceBundle bundle = ResourceBundle.getBundle(context.getApplication().getMessageBundle(), L, loader);
//Wyszukujemy w pliku napisu o id = javax.faces.component.UIInput.REQUIRED.
String resource = bundle.getString("javax.faces.component.UIInput.REQUIRED");
//Tworzymy obiekt wyświetlany w ice:message zastępując znak {0}
// wartością id danej ice:inputText.
FacesMessage f = new FacesMessage(FacesMessage.SEVERITY_ERROR, resource.replace("{0}", component.getId()),
resource.replace("{0}", component.getId()));
throw new ConverterException(f);
}
//Jeżeli Name zawiera tylko litery.
if (value.matches("[A-Za-z]+"))
return new Name(value);
else
{
//Przypadek gdy ktoś w polu imię podał cyfrę.
//Ten sam sposób na na początku.
Locale L = context.getViewRoot().getLocale();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
ResourceBundle bundle = ResourceBundle.getBundle(context.getApplication().getMessageBundle(), L, loader);
String resource = bundle.getString("robpie.Name");
FacesMessage f = new FacesMessage(FacesMessage.SEVERITY_ERROR, resource.replace("{0}", component.getId()),
resource.replace("{0}", component.getId()));
throw new ConverterException(f);
}
}

public String getAsString(FacesContext context, UIComponent component, Object value) {
return value.toString();
}

}
//-----------------------------------------
import java.util.Locale;
import java.util.ResourceBundle;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;

/**
*
* @author riaa
*/
public class SurnameConverter implements Converter{

public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value.isEmpty())
{
Locale L = context.getViewRoot().getLocale();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
ResourceBundle bundle = ResourceBundle.getBundle(context.getApplication().getMessageBundle(), L, loader);
String resource = bundle.getString("javax.faces.component.UIInput.REQUIRED");
FacesMessage f = new FacesMessage(FacesMessage.SEVERITY_ERROR, resource.replace("{0}", component.getId()),
resource.replace("{0}", component.getId()));
throw new ConverterException(f);
}
if (value.matches("[A-Za-z]"))
{
return new Surname(value);
}
else
{
//Przypadek gdy ktoś w polu imię podał cyfrę.
Locale L = context.getViewRoot().getLocale();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
ResourceBundle bundle = ResourceBundle.getBundle(context.getApplication().getMessageBundle(), L, loader);
String resource = bundle.getString("robpie.Surname");
FacesMessage f = new FacesMessage(FacesMessage.SEVERITY_ERROR, resource.replace("{0}", component.getId()),
resource.replace("{0}", component.getId()));
throw new ConverterException(f);
}
}

public String getAsString(FacesContext context, UIComponent component, Object value) {
return value.toString();
}

}
//---------------------------------
import java.util.Locale;
import java.util.ResourceBundle;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;

/**
*
* @author riaa
*/
public class ZipCodeConverter implements Converter{

public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value.matches("[0-9][0-9][-][0-9][0-9][0-9]"))
return  new ZipCode(value);
else
{
Locale L = context.getViewRoot().getLocale();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
ResourceBundle bundle = ResourceBundle.getBundle(context.getApplication().getMessageBundle(), L, loader);
String resource = bundle.getString("robpie.ZipCode");
FacesMessage f = new FacesMessage(FacesMessage.SEVERITY_ERROR, resource, resource);
throw new ConverterException(f);
}
}

public String getAsString(FacesContext context, UIComponent component, Object value) {
return value.toString();
}
}
Teraz do każdej kontrolki dodajmy znacznik f:converter.:
<h:form prependId="false">
<table>
<tr>
<td>Imię:</td>
<td><ice:inputText id="name" value="#{registrationForm2.name}">
<f:converter converterId="NameConverter" />
<f:validateLength minimum="5" maximum="20" />
</ice:inputText></td>
<td><ice:message for="name" /></td>
</tr>
<tr>
<td>Nazwisko:</td>
<td><ice:inputText id="surname" value="#{registrationForm2.surname}" >
<f:validateLength minimum="5" maximum="20" />
</ice:inputText></td>
<td><ice:message for="surname" /></td>
</tr>
<tr>
<td>Kod pocztowy:</td>
<td><ice:inputText id="zipCode" value="#{registrationForm2.zipCode}" required="true">
<f:converter converterId="ZipConverter" />
</ice:inputText></td>
<td><ice:message for="zipCode" /></td>
</tr>
</table>
<ice:commandButton value="Dalej" action="registration2"/>
</h:form>

A na sam koniec musimy w pliku faces-config.xml dodać znacznik converter z odpowiednimi wpisami:

<converter>
<converter-id>ZipConverter</converter-id>
<converter-class>robpie.ZipCodeConverter</converter-class>
</converter>
<converter>
<converter-id>NameConverter</converter-id>
<converter-class>robpie.NameConverter</converter-class>
</converter>
<converter>
<converter-for-class>robpie.Surname</converter-for-class>
<converter-class>robpie.SurnameConverter</converter-class>
</converter>


Teraz spróbujmy przetestować formularz:

Thursday, November 5, 2009

Konwersja < i > na encje htm'a lt i gt

Dla osób, które często korzystają z SyntaxHightlightera na pewno się przyda prosty program, który zamieni wszystkie znaczniki < > na odpowiednie &lt i &gt.
Pozdrawiam bloggerów.

Konwersja i weryfikacja przy pomocy standardowych kontrolek JSF

Nadszedł czas omówić konwersję i weryfikację danych w aplikacji JavaServer Faces przy użyciu standardowych mechanizmów przygotowanych przez twórców JSF. Przykład ten będzie na tyle prosty aby każdy mógł zrozumieć naturę standardowych mechanizmów kontroli wpisywanych danych przez użytkownika, a następnie pozwoli nam utworzyć własne klasy implementujące odpowiednie interfejsy. No to zaczynamy:

1) Utwórzmy formularz, który będzie zawierać następujące pola tekstowe:

Za informację przechowywane w każdym polu będzie odpowiadać klasa RegistrationForm:

public class RegistrationForm {
private String name;
private String surname;
private Date dateOfBirth;
private int age;
private double doubleValue;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getSurname() {
return surname;
}

public void setSurname(String surname) {
this.surname = surname;
}


public Date getDateOfBirth() {
return dateOfBirth;
}

public void setDateOfBirth(Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public double getDoubleValue() {
return doubleValue;
}

public void setDoubleValue(double doubleValue) {
this.doubleValue = doubleValue;
}

}
Natomiast nasz kod strony powinien na początku wyglądać następująco:

<h:form prependId="false">
<table>
<tr>
<td>Imię:</td>
<td><ice:inputText id="name" value="#{registrationForm.name}" >
</ice:inputText></td>
<td></td>
</tr>
<tr>
<td>Nazwisko:</td>
<td><ice:inputText id="surname" value="#{registrationForm.surname}" >
</ice:inputText></td>
<td></td>
</tr>
<tr>
<td>Data urodzenia:</td>
<td>
<ice:selectInputDate id="dateOfBirth"
value="#{registrationForm.dateOfBirth}">
</ice:selectInputDate> </td>
<td></td>
</tr>
<tr>
<td>Wiek:</td>
<td><ice:inputText id="age" value="#{registrationForm.age}" >
</ice:inputText></td>
<td></td>
</tr>
<tr>
<td>Wartość double:</td>
<td><ice:inputText id="doubleValue" value="#{registrationForm.doubleValue}">
</ice:inputText></td>
<td></td>
</tr>
</table>
</h:form>

<ice:commandButton value="Dalej" action="registration"/>
Teraz utwórzmy drugą stronę RegistrationComplete.jspx i utwórzmy w pliku faces-config.xml regułę nawigacji pomiędzy obiema stronami.
Strona RegistrationComplete.jspx:

<jsp:root version="2.1"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:ice="http://www.icesoft.com/icefaces/component">
<jsp:directive.page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"/>
<f:view>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>RegistrationComplete.jspx</title>
</head>
<body>
<h1>Dziękujemy za rejestrację!</h1>
<ice:form>

</ice:form>
</body>
</html>
</f:view>
</jsp:root>
Reguła nawigacji:

<navigation-rule>
<from-view-id>/robpie/RegistrationFormIFaces.jspx</from-view-id>
<navigation-case>
<from-outcome>registration</from-outcome>
<to-view-id>/robpie/RegistrationComplete.jspx</to-view-id>
</navigation-case>
</navigation-rule>
Czas na przetestowanie czy reguła nawigacji działa. Uruchamiamy projekt i przechodzimy na stronę: RegistrationFormIFaces.jspx i klikamy przycisk "Dalej". Powinniśmy ujrzeć drugą stronę.

Weryfikacja i konwersja.

Każda kontrolka posiada atrybut required, który oznacza czy pole jest wymagane. np:

<ice:inputText id="name" value="#{registrationForm.name}" required="true">
</ice:inputText>
<ice:inputText id="age" value="#{registrationForm.age}" > </ice:inputText>
Oznacza to, że jeśli nie uzupełnimy danego pola to nie będziemy mogli przejść na drugą stronę. Teraz dodajmy jeszcze jeden znacznik, który poinformuje nas o napotkanych błędach ponieważ standardowo błędy tego typu nie są wyświetlane na stronie (Wyświetlają się w logach serwera Glassfish). W każdy wolny znacznik wstawmy następujący kod:


<ice:message for="name" />
<ice:message for="surname" />
<ice:message for="dateOfBirth" />
<ice:message for="age" />
<ice:message for="doubleValue" />


Atrybut for lokalizuje nam dla jakiego identyfikatora id kontrolki wejściowej ma wyświetlać błędy.
Teraz nasza strona powinna wyglądać następująco:


<h:form prependId="false">
<table>
<tr>
<td>Imię:</td>
<td><ice:inputText id="name" value="#{registrationForm.name}" required="true">
</ice:inputText></td>
<td><ice:message for="name" /></td>
</tr>
<tr>
<td>Nazwisko:</td>
<td><ice:inputText id="surname" value="#{registrationForm.surname}" required="true">
</ice:inputText></td>
<td><ice:message for="surname" /></td>
</tr>
<tr>
<td>Data urodzenia:</td>
<td>
<ice:selectInputDate id="dateOfBirth"
value="#{registrationForm.dateOfBirth}"
required="true">
</ice:selectInputDate> </td>
<td><ice:message for="dateOfBirth" /></td>
</tr>
<tr>
<td>Wiek:</td>
<td><ice:inputText id="age" value="#{registrationForm.age}" >
</ice:inputText></td>
<td><ice:message for="age" /></td>
</tr>
<tr>
<td>Wartość double:</td>
<td><ice:inputText id="doubleValue" value="#{registrationForm.doubleValue}">
</ice:inputText></td>
<td><ice:message for="doubleValue" /></td>
</tr>
</table>
<ice:commandButton value="Dalej" action="registration"/>
</h:form>
W tym momencie czas przetestować naszą aplikację. Uruchamiamy ją i przechodzimy na stronę z rejestracją. Klikamy "Dalej". Widać, że w tym momencie wyświetlają się pewne komunikaty, które informują, że pole jest wymagane.
Validation Error: Value is required.

Teraz dodamy kolejne validatory, a na końcu pokażę jak zmienić standardowe komunikaty frameworku JavaServer Faces.
Dodamy:


<f:validateLength minimum="5" maximum="20" />
<f:convertDateTime pattern="dd/MM/yyyy" />
<f:validateLongRange minimum="18" maximum="98" />
<f:validateDoubleRange minimum="3.14" maximum="18.5" />

1)Weryfikacja długości łańcucha znakowego.
2)Konwersja łańcucha znakowego na typ Date po stronie serwera.
3)Weryfikacja przedziału liczb całkowitoliczbowych.
4)Weryfikacja przedziałów liczb zmiennoprzecinkowych.
Odpowiednie kontrolki dodajmy między znaczniki inputText i selectInputDate:



<ice:inputText id="name" value="#{registrationForm.name}" required="true">
<f:validateLength minimum="5" maximum="20" />
</ice:inputText>
<ice:inputText id="surname" value="#{registrationForm.surname}" required="true">
<f:validateLength minimum="5" maximum="20" />
</ice:inputText>
<ice:selectInputDate id="dateOfBirth"
value="#{registrationForm.dateOfBirth}"
renderAsPopup="true"
required="true">
<f:convertDateTime pattern="dd/MM/yyyy" />
</ice:selectInputDate>
<ice:inputText id="age" value="#{registrationForm.age}" >
<f:validateLongRange minimum="18" maximum="98" />
</ice:inputText>
<ice:inputText id="doubleValue" value="#{registrationForm.doubleValue}">
<f:validateDoubleRange minimum="3.14" maximum="18.5" />
</ice:inputText>
Zwiększyliśmy restrykcje weryfikacji i nieporawne dane zostają wykryte a odpowiednie błędy są wyświetlane.
Powinniśmy teraz zmienić treść każdego z błędów na taki, który nam odpowiada.
Zaletą JSF jest to, że wszystkie komunikaty przechowywujemy w jednym miejscu. Każdy komunikat jest zmienną odpowiedniej klasy. Raz zdefiniowane błędy będą dostępne w każdym formularzu i na każdej stronie. W przeciwieństwie do ASP.NET nie musimy klepać wszędzie errorMessage w każdym miejscu.
Musimy utworzyć odpowiedni plik np:
myMessage.properties i tam umieścić nasze komunikaty:
Klikamy na swoją paczkę PPM wybieramy New -> Other -> Other -> Properties File.
Klikamy PPM na plik myMessages.properties i klikamy Edit.
Wklejamy następujący kod:



javax.faces.component.UIInput.REQUIRED=<b><span >*Pole {0} obowiązkowe.</span></b>
javax.faces.validator.LongRangeValidator.NOT_IN_RANGE=<b><span style="color:red">*Wartość {2} musi być w przedziale od {0} do {1}.</span></b>
javax.faces.validator.DoubleRangeValidator.NOT_IN_RANGE=<b><span >*Wartość {2} musi być w przedziale od {0} do {1}.</span></b>
javax.faces.converter.DateTimeConverter.CONVERTER=Please enter a valid date.
javax.faces.converter.DateTimeConverter.DATE=<b><span >Podaj poprawny format daty: {1}</span></b>
javax.faces.component.UIInput.CONVERSION=<b><span style="color:red">*Błąd konwersji.</span></b>
javax.faces.validator.LengthValidator.MINIMUM=<b><span >*Pole {1} musi być większe od {0}</span></b>
javax.faces.validator.LengthValidator.MAXIMUM=<b><span style="color:red">*Pole {1} musi być mniejsze od {0}</span></b>

Ostatnim krokiem będzie dodanie odpowiedniego wpisu do pliku faces-config.xml informującego gdzie framework ma szukać pliku z komunikatami.

Uruchamiamy aplikację i testujemy działanie.



<application>
<message-bundle>robpie.myproperties</message-bundle>
</application>

Utworzenie pustej strony frameworku ICEFaces.

Zaczynając pracę w frameworku JavaServer Faces szybko dostrzegamy jak ubogi jest zestaw kontrolek, którymi możemy się posłużyć. Niemniej jednak świat aplikacji web w Java wygląda zachęcająco. Istnieje mnóstwo szkieletów aplikacji ulepszających JSF np ciekawy projekt http://www.icefaces.org/main/home/ . Tak naprawdę ICEFaces jest zestawem ciekawych kontrolek i rozszerza FacesServlet przez co jest łatwy do implementacji i do zrozumienia. Tyle słowem wstępu. Zacznijmy pracę.

Tworzenie strony ICEFaces:

W tym miejscu zakładam, że poprawnie zainstalowałeś moduły NB dające mozliwość współpracy z tym ciekawym projektem.
Pierwszym krokiem jest wybranie PPM new -> JSF JSP Page.
Naszym oczom pojawi się następujące okno:

Musimy wybrać opcję JSP Document(XML Syntax) i kliknąć finish. Naszym oczom pojawi sie nowa strona o rozszerzeniu *.jspx. Strony o tym rozszerzeniu są de facto plikami xml-owymi co pozwala nam na łatwiejsze diagnozowanie błędów jak w przypadku Facelets. Teraz abyśmy mogli używać w NB komponentów przekazać informację o xml namespace odnośnie biblioteki ICEFaces. Wygląda to następująco:


<jsp:root version="2.1"
f="http://java.sun.com/jsf/core"
h="http://java.sun.com/jsf/html"
jsp="http://java.sun.com/JSP/Page"
ice="http://www.icesoft.com/icefaces/component">
</jsp:root>
<jsp:directive.page contentType="text/html;
charset=UTF-8" pageEncoding="UTF-8"/>
<f:view>
<ice:form>

<ice:form>
</f:view>


Teraz pomiędzy znacznikami wrzucamy co nam tylko przyjdzie do głowy.
Pozdrawiam.

Sunday, September 6, 2009

Uruchamianie php.exe z dowolnego miejsca w cmd.exe

php

Dziś trochę zboczę z publikacji nt. java i szybko opiszę jak uzyskać możliwość uruchomienia parsera PHP z dowolnej lokalizacji w Wierszu Poleceń naszego Okna na świat. Sprawa jest banalnie prosta(co prawda nie była banalna gdy nie wpadłem na olśnienie...). Otóż musimy pokusić się o poznanie tak zwanych zmiennych środowiskowych systemu Windows (List of Windows environment variables) i przyjrzeć się zmiennej o nazwie PATH. Zmienna ta przechowuje listę ścieżek z plikami wykonywalnymi *.exe,*.bat,*.com np. C:\Windows\[notepad.exe], które następnie są przeszukiwane podczas pracy z wierszem poleceń i uruchamiają program wykonywalny. Zatem aby móc korzystać z php w dowolnym miejscu należy uzupełnić zmienna PATH dodając miejsce gdzie znajdują się pliki *.exe katalogu php. W moim przypadku wygląda to następująco:
D:\wamp\bin\php\php5.2.9-2\.

Dodanie ścieżki z php do zmiennej środowiskowej:

Otwórz Panel Sterowania, następnie System, kliknij zakładkę Zaawansowane, na dole powinien być przycisk Zmienne środowiskowe, po kliknięciu go wyszukaj zmienną PATH i edytuj , następnie w oknie dialogowym poprzedzając średnikiem dodaj swoją ścieżkę php
;D:\wamp\bin\php\php5.2.9-2\. Zapisz wszystkie zmiany i uruchom ponownie wiersz poleceń. Przetestuj wiersz poleceń wpisując:

php -r phpinfo();

Voilla!

Wednesday, August 26, 2009

Rysowanie wykresów w html.

Niezależnie od technologii, w której będziemy programować zdarza się czasem potrzeba wyświetlenia informacji w postaci wykresów. Czy to pChart dla php, a może ICEFaces i odpowiednie znaczniki. Programista ma duży dostęp do gotowych bibliotek w każdym języku programowania. Wyobraźmy sobie, że nauczyłem się specyfikacji pChartu czy JFreeChart i moje aplikacje tworzą piękne wykresy. Jednak co się zdarzy gdy jestem zmuszony skorzystać z technologii, w której nie znam podstaw, a pracodawca każe utworzyć strony z wykresami? Czas goni, nerwy się wdzierają w każdy fragment kodu, a my niechlujnie zapisujemy różne metody i klasy. Nie testujemy bo nie ma czasu... Dodatkowo generowanie danych binarnych obciąża nasze serwery i zapycha ruch.

No i co począć?

Znalazłem ciekawe rozwiązanie, a dokładniej Google Chart API. Dzięki temu narzędziu nie musimy martwić się o znajomość danego czy innego języka. Idea tworzenia wykresów polega na wysłaniu porcji danych(które chcemy wizualizować) za pomocą metody GET . Adres, który utworzyliśmy i podpinamy pod odpowiedni znacznik img , h:graphicImage lub dowolnie innego.

Przykłady:

a


a

a

a


Nawet mapy można łatwo tworzyć:

a

servlet-name,servlet-mapping, url-pattern i /faces

Kilka dni temu gdy kolejny raz przeglądałem książkę o JavaServer Faces przed snem doznałem olśnienia dlaczego trzeba wpisywać http:// localhost:8080/mojaAplikacja/faces/strona.jsp aby strona strona.jsp wyświetliła prawidłowo. Chciałbym zaprezentować przykład krok po kroku, a w trakcie będę tłumaczył uzyskane wyniki. Przykład zrealizuję w środowisku Netbeans 6.7.1
Zatem zaczynamy:

*Dodaj framework JavaServer Faces i kliknij finish.

Po stworzeniu przykładowej aplikacji ServletFaces otrzymujemy następującą strukurę katalogów:



Gdy uruchomimy aplikację wyświetli się nam w oknie przeglądarki adres:

Teraz spróbujemy dodać stronę jsp np. page2.jsp. Struktura katalogów powinna wyglądać tak:


Uzupełnij stronę tak abyś miał pewność, że znajdujesz się na stronie page2.jsp. Ja uzupełniłem ją w następujący sposób:



Teraz będziemy starali się poprzez adres w przeglądarce wyświetlić treść nowo utworzonej strony. Pierwszym pomysłem byłoby wpisanie następującego adresu:


Jeżeli odwiedzasz ten artykuł to z pewnością wiesz jaki będzie rezultat. Pytanie tylko dlaczego?

org.apache.jasper.JasperException: java.lang.RuntimeException: Cannot find FacesContext
W skrócie:

Powodem tego jest sposób wdrażania aplikacji internetowych, które są tak naprawdę procesem żmudnym. Trzeba utworzyć odpowiednią strukturę katalogów (patrz powyżej) oraz odpowiednio skonfigurować pliki (*.xml) aplikacji. Za dynamiczną treść prezentowaną na stronach jsp są odpowiedzialne Servlety. Servlety to klasy java, które przetwarzają zlecenia klientów.

Servlety konfiguruje się w plikach xml podając dane potrzebne do uruchomienia. Zajrzyj do pliku web.xml. W tym pliku znajduje się odpowiedź dlaczego wcześniejsze http://localhost:8080/ServletFaces/page2.jsp wywołało wyjątek.

Oto odpowiedź:


Widzimy jak się prezentuje nazwa servletu: Faces Servlet oraz, która klasa implementuje metody inicjalizujące, wykonujące żądania oraz "niszczące" dany servlet:
javax.faces.webapp.FacesServlet.
Podświetlony na żółto znacznik <servlet-mapping/> odpowiada za "uruchamianie" i wykonywanie kodu FacesServlet.

<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>

Powyższy kod mówi nam: że Faces Servlet będzie uruchamiany w przeglądarce jeśli wpiszemy adres zawierający url-pattern. Na przykład aby wyświetlić wcześniejszą stronę page2.jsp musimy wpisać następujący adres:


Teraz skoro rozwiązaliśmy początkowy problem dodamy nową regułę url-pattern, która nieco zmieni sposób w jaki będziemy mogli uruchomić serwlet. Do pliku web.xml w miejscu zakończenia poprzedniego tagu servlet-mapping dodaj następujący kod:


Teraz zamiast pisać:
Możemy alternatywnie wywołać stronę jsp w poniższy sposób:

Dziękuję za uwagę :-)


Benone!

Wednesday, August 12, 2009

Poziomy izolacji tranzakcji i Hibernate.

Próbując sił z Hibernate musiałem natknąć się na problem zwany Tranzakcje i Sesje. Ku mojemu zdziwieniu znalazłem na polskich stronach ciekawe omówienie tematyki tranzakcji SQL, które są "kręgosłupem" podczas programowania w Java i Hibernate. Podsyłam link:

Sunday, August 9, 2009

ASP.NET , C#, LINQ to SQL i unikalne rekordy z tabeli.

Istnieje wiele różnych sposobów, które są lepsze lub gorsze aczkolwiek ja przedstawię taki, który jest bardzo szybki w implementacji(w działaniu pewnie odwrotnie proporcjonalny). Pierwszą rzeczą, którą należy wykonać to pobrać wszystkie rekordy z bazy danych np przy pomocy LINQ to SQL Classes.

Przykładowo jeśli mamy tabelę w MSSQL o nazwie np. Odleglosci, która posiada primary key ID, oraz kolumnę miastoA, miastoB oraz odleglosci.
Pobieram wszystkie (powtarząjące się) rekordy miastoA:

var miasta = (from ex in mojDataContext.Odlegloscis select ex.miastoA);

Następnie skorzystamy z klasy HashSet, która jest kontenerem reprezentującym zbiór elementów w naszym przypadku obiektów String.
HashSet<String> uniqeMiasta = new HashSet();
Następnie przejdziemy po każdym "rekordzie" wynikowym z zmiennej miasta:

foreach (String o in Miasta)
{
uniqeMiasta.Add(o);
}


Następnym krokiem(ostatnim) będzie utworzenie tablicy oraz posortowanie jej alfabetycznie.
String[] miasta = uniqeMiasta.ToArray();
Array.Sort(miasta);


Wyjaśnię teraz na czym polega fenomen obiektów HashSet. Po prostu implementacja tej klasy uniemożliwia wstawienie dwóch identycznych wartości poprzez metodę .Add(Object o).

Dziękuję za uwagę.

Hibernate, pobranie kilku kolumn z tabeli

Dziś opiszę trochę technologię Hibernate czyli frameworka, który zajmuje się mapowaniem obiektów Java na tabele z bazy danych. O ile pobranie zawartości całej tabeli nie jest trudnym zadaniem to wyświetlenie kilku kolumn może przysporzyć programiście trochę pracy. Najpierw szybko przypomnę jak pobrać i wyświetlić całą tabelę, a następnie pokażę jak pobrać poszczególne kolumny.

Zakładam, że klasa HibernateUtil, klasy odwzorowywujące tabele, pliki hbm.xml, są już utworzone w Twoim projekcie.
Dla naszego przykładu zdefiniujmy klasy java np.: Osoba

public class Osoba implements Serializable
{
  private int ID_Osoby;
  private String imie;
  private String nazwisko;
  public Osoba(){}//Domyślny konstruktor JavaBeans.
  // settery i gettery dla każdego 
  pola.:getImie,setImie,getID_Osoba,set_ID_Osoba,setNazwisko,getNazwisko;
}

Zatem po pierwsze tworzymy obiekt sesji i tranzakcji Hibernate:

  Session session = HibernateUtil.getSessionFactory().getCurrentSession();
  Transaction tx = session.beginTransaction();
  tx.begin();
Czas teraz przygotować odpowiednie zapytanie hsql, które pobierze całą tabelę: from Osoba.

  Query Q = session.createQuery("from Osoba");
  ArrayList<Osoba> list= new ArrayList<Osoba>();
  list = (ArrayList<Osoba>) Q.list();
  tx.commit();
Widać, że utworzyłem zmienną list, która jest typu ArrayList z parametrem Osoba. Parametrem jest typ Osoba , ponieważ pobrałem wszystkie kolumny tabeli. Możemy teraz pobrać list.get(int i) poszczególną Osobę i wyświetlić jej informacje na konsoli:
for (int i=0;i<list.size();i++)
{
  Osoba tempOsoba = list.get(i);
  System.out.println(tempOsoba.getID() + " " + tempOsoba.getImie() + " " +   
  tempOsoba.getNazwisko());
}
To był tylko wstęp. Teraz przejdziemy do prawdziwego zadania czyli wyświetlenie 2 kolumn imie oraz nazwisko z tabeli Osoba.

  Session session = HibernateUtil.getSessionFactory().getCurrentSession();
  Transaction tx = session.beginTransaction();
  tx.begin();
  Query Q = session.createQuery("select imie,nazwisko from Osoba");
  //Można zapisać "select osoba.imie,osoba.nazwisko from Osoba as osoba" - bardziej obiektowo :-)
  ArrayList<Object[]>list = new ArrayList<Object[]>;
  for (int i=0;i<list.size();i++)
  {
    Object temp[] = list.get(i);
    System.out.println(temp[0] + " " + temp[1]);
  }

Przydało by się wyjaśnić powyższy fragment kodu. Chodzi o to, że jeśli wybieramy kilka kolumn to implementacja Hibernate najprościej ujmując każdą kolumnę(każdy rekord kolumny jest typu Object) pakuje w obiekt Object. Jeżeli po klauzuli select wystąpią dwie[lub więcej] kolumny np : imie, nazwisko to automatycznie te dwie wartości są udostępniane jako tablica Object[] a dostęp do pól mamy za pomocą indeksów. Dlatego musimy utworzyć odpowiednią listę ArrayList<Object[]> do odebrania danych z rekordów z tabeli.

Można też użyć iteratora dla listy:

for (Iterator i = list.iterator();i.hasNext();){
  Object[] result = (Object[]) i.next();
  System.out.println(result[0] + " " + result[1] );
}

Dziękuję za uwagę - Robert.

Wednesday, August 5, 2009

Zapisanie, serializacja obiektów Java


Środowisko programistyczne java pozwala na kilka sposobów zapisania obiektu do "źródła", które po zamknięciu programu będzie przechowywać wartości obiektów. Do głowy przychodzą mi dwa pomysły

  • łatwy - zapisać stan obiektu w pliku tekstowym zapisując każde pole

  • łatwiejszy - wykorzystać mechanizm serializacji obiektu.


  • Mając na myśli łatwiejszy rozważam tutaj korzyści z serializacji (szybkości implementacji oraz elastyczności programowania).

    Serializacja obiektów w java jest swego rodzaju zrzutem obiektu z pamięci RAM to strumienia bajtowego zapisanego na np dysku twardym. Do tego rodzaju operacji są wykorzystywane klasy strumieniowe : ObjectOutputStream, ObjectInputStream oraz odpowiednie metody void writeObject(Object o), Object readObject(). Standardowo klasy, które są dostępne w javie od razu posiadają metody do zapisu/odczytu wartości danego typu oraz co ważne: implementują intefrejs Serializable np:
    writeUTF(String s) / readUTF()

    writeFloat(float f) / readFloat()
    writeDobule(double d) / read Double()

    etc...

    Zatem jak zapisać/odczytać ?



    Podrzucam spakowany projekt NetBeans SerializationExample.

    Najpierw trzeba utworzyć obiekt ObjectOutputStream aby móc w nim zapisać dane, które nas interesują.



    // Metoda do zapisu danych w pliku test.temp
    try
    {
    ObjectOutputStream MyObjectOutputStream = new ObjectOutputStream(new FileOutputStream("test.temp"));
    MyObjectOutputStream.writeUTF(serializujTextField.getText());
    MyObjectOutputStream.close();
    }
    catch (IOException ex)
    {
    ex.printStackTrace();
    }


    Następnie gdy zapisaliśmy w pliku test.temp spróbujmy zamknąć program, a po uruchomieniu wcisnąć przycisk odczytaj.
    Udostępnię teraz kod, który odczytuje z pliku test.temp naszą zapisaną wartość w String.


    try {
    ObjectInputStream OIS = new ObjectInputStream(new FileInputStream("test.temp"));
    serializujLabel.setText(OIS.readUTF());
    OIS.close();
    }
    catch (IOException ex)
    {

    }


    Działa !

    Jednak dla bardziej złożonych problemów nie wystarczy zapisać samego pola obiektu (String), a trzeba zapisać cały obiekt własnej klasy wraz z jego wszystkimi polami typów prostych (int,char,boolean,float,itp) oraz pola referencyjne (MojaJakasKlasa,JFrame,JButton, itp itp). Zatem stwórzmy klasę o nazwie Osoba, która będzie przechowywać dwa pola imię, nazwisko.

    Przykładowy kod klasy z setterami, getterami i konstruktorem:



    public class Osoba{

    private String imie;
    private String nazwisko;

    public String getImie() {
    return imie;
    }

    public void setImie(String imie) {
    this.imie = imie;
    }

    public String getNazwisko() {
    return nazwisko;
    }

    public void setNazwisko(String nazwisko) {
    this.nazwisko = nazwisko;
    }


    public Osoba(String imie, String nazwisko) {
    this.imie = imie;
    this.nazwisko = nazwisko;
    }
    }


    Skoro dodałem już klasę Osoba to niech obiekt tej klasy znajdzie się w głównej formie MyJFrame i zostanie utworzony jeśli naciśniemy przycisk serializuj w panelu Serializacja obiektu Osoba. Następnie zamknijmy program i włączmy go ponownie i kliknijmy odczytaj. Widać, że nie możemy odczytać z ObjectInputStream ponieważ nasza klasa nie implementuje intefrejsu Serializable.
    Powstanie wyjątek typu:

    java.io.InvalidClassException: serializationexample.Osoba; serializationexample.Osoba; class invalid for deserialization


    Zatem musimy zmienić nagłówek klasy na następujący:


    public class Osoba implements Serializable
    .
    Zapiszmy zmiany w projekcie, utwórzmy obiekt osoba wpisując imię i nazwisko w polach tekstowych, kliknijmy serializuj, następnie zamknijmy, otwórzmy program i wciśnijmy przycisk odczytaj. Widać, że program już działa jak powinien.
    Jedyną znaczną różnicą wobec przykładu z typem String jest to, że musimy dokonać konwersji zawężającej do typu Osoba:




    o = (Osoba) MyObjectInputStreamOsoba.readObject();
    MyObjectInputStreamOsoba.close();
    imieLabel.setText(o.getImie());
    nazwiskoLabel.setText(o.getNazwisko());




    Dodam na koniec ciekawostkę, że intefrejs Serializable nie posiada żadnych metod do przedefiniowania i nazywa się znacznikowym intefrejsem. Potrzeba istnienia takich intefrejsów polega na tym, że wyznaczają typ naszej klasy:

    if (O instance of Serializable)

    Powyższa linijka kodu zwróci wartość true, a kompilator będzie wiedział, że obiekty można zapisywać/odczywać.

    Jeżeli mamy rozbudowaną hierarchię dziedziczenia oraz klasa Osoba posiada inne pola obiektowe np. Adres a, HistoriaChoroby h, to nie musimy się martwić o zapisanie tych pól albowiem java rekurencyjnie zapisuje wszystkie pola klasy (muszą oczywiście implementować interfejs Serializable).

    Jeśli mamy bardzo rozbudowaną klasę, która używa mechanizmu szeregowania(serializacji), a nie podoba się nam, że wszystkie pola są zapisywane możemy w prosty sposób powiedzieć kompilatorowi aby nie zapisywał danego pola klasy. Do tego celu musimy użyć słowa kluczowego javy transient przykład:


    private transient String maloWaznaZmienna;

    Orginalny post z mojej domeny rpiesnikowski.pl

    Dziękuję za uwagę i pozdrawiam
    Robert.

Hibernate, <h:dataTable> i usuwanie rekordów

Po tylu godzinach spędzonych przy debugowaniu i szukaniu rozwiązania na mój problem dotyczący połączenia frameworka Hibernate oraz JSF dataTable pomyślałem sobie, że napiszę quick how-to odnośnie tego problemu.

Problem podzielę na kilka części:
  1. Pierwszą częścią będzie utworzenie relacji w bazie mysql-owej i utworzenie odpowiednich tabel do dalszej pracy.
  2. Utworzenie aplikacji w NetBeans oraz konfiguracja Hibernate.
  3. Ostatnia część będzie odpowiedzialna za właściwy problem. Tutaj udostępnię kod i go wytłumaczę.
*Jeżeli umiesz wykonać daną część to spokojnie możesz ją pominąć.


Część 1.

Szukając odpowiedzi na tworzenie relacji w mysql napotkałem się na bardzo treściwą stronę,
na której jest wszystko naprawdę ładnie wytłumaczone. Zapraszam do kliknięcia poniżej.


No dobra to napewno już masz utworzony widok relacyjny w phpmyadmin. Teraz trzeba utworzyć bazę danych (nazwałem ją hibernate) oraz tabele (person, orders).

CREATE TABLE IF NOT EXISTS `person` (
`ID_PERSON` int(11) NOT NULL AUTO_INCREMENT,
`NAME` varchar(100) COLLATE utf8_polish_ci NOT NULL,
`SURNAME` varchar(100) COLLATE utf8_polish_ci NOT NULL,
PRIMARY KEY (`ID_PERSON`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci AUTO_INCREMENT=32 ;


Tabela orders:
CREATE TABLE IF NOT EXISTS `orders` (
`ID_ORDER` int(11) NOT NULL AUTO_INCREMENT,
`ID_PERSON` int(11) NOT NULL,
`TITLE` text COLLATE utf8_polish_ci NOT NULL,
`COST` float NOT NULL,
PRIMARY KEY (`ID_ORDER`),
KEY `ID_PERSON` (`ID_PERSON`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci AUTO_INCREMENT=1 ;

// utworzenie odpowiedniej relacji między person.ID_Person (a kluczem obcym)<->
orders.ID_Person

ALTER TABLE `orders`
ADD CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`ID_PERSON`) REFERENCES `person` (`ID_PERSON`) ON DELETE CASCADE ON UPDATE CASCADE;
Teraz gdy masz puste tabele to przejdź w phpmyadmin do tabeli orders i spróbuj dodać nowy rekord. Jeżeli w tabeli person nie ma rekordu to wystąpi błąd sql ponieważ relacja między tabelami nie pozwala na dodawanie do orders rekordów gdy person nie ma danych.
Lekko zboczyliśmy z kursu, pardon.
Mała rada:
Dodaj do tabeli person jak najwięcej osób, a następnie kliknij zakładkę eksport i wyeksportuj dodane rekordy do pliku person.sql. Przyda Ci się to przy testowaniu gdy zabraknie rekordów w tabeli nie będziesz musiał ręcznie ich wprowadzać tylko uruchomisz plik person.sql, a tabela automatycznie będzie znów pełna.

Część 2.

Zatem czas przystąpić do uruchomienia NB (ja aktualnie pracuje w wersji 6.7).
Kliknij: New Project -> Java Web -> Web Application -> nazwa projektu: hibernateOnMysql -> Server and Settings : Server Glassfish 2.1, EE Version: Java EE 5 -> Frameworks : JavaServer Faces i Hibernate 3.2.5, ustaw Database Connection na : jdbc:mysql://localhost:3306/hibernate -> Finish.

Gdy kliknąłeś finish powinien pojawić Ci się plik hibernate.cfg.xml : kliknij zakładkę: Miscellaneous Properties, add -> Property name : hibernate.current_session_context_class, property value : Thread.
Teraz przejdź do zakładki : Configuration Properties -> add : Property Name: hibernate.show_sql, property value : true .

Pierwszy krok mamy za sobą teraz trzeba utworzyć kolejne pliki konfiguracyjne:

Wybierz zakładkę hibernate:


Dalej add all table:



Naszym oczom pokazał się plik: hibernate.revenge.xml ,z którego widać, które tabele będą następnie poddane mapowaniu.

Teraz netbeans automatycznie utworzy klasy Java oraz pliki hbm.xml dla każdej tabeli z bazy danych. W tym celu ponownie kliknij new -> other -> hibernate i wybierz New Hibernate Mapping Files and POJOs from Database.


W package wpisz hibernate . Kliknij finish.













Otwórz zakładkę Source Packages i hibernate i otwórz klasy Person.java i Orders.java i odpowiadające im pliki hbm.xml :


Możesz zauważyć jak wygląda mapowanie klas Java z tabelami z bazy hibernate.

Konfigurację aplikacji zakończymy dodając do faces-config.xml odpowiednie klasy:

Część 3.

W tym miejscu będziemy musieli na stronie JSP dodać znacznik a w nim zagnieździć odpowiednio oraz zaimplementujemy klasę MyDataModel, która będzie dziedziczyć ListDataModel i zaimplementujemy metodę interfejsu javax.faces.event.ActionListener o nazwie processAction().

Zatem tak powinien wyglądać komponent :



Musimy zdefiniować atrybut value i związać go z własnością tableValues klasy MyDataModel. W naszej klasie będzie to obiekt ArrayList<Person>, który będzie przechowywał wszystkie osoby pobrane z tabeli Person. Oznaczamy tableValues zmienną osoba. W każdej kolumnie tworzymy znacznik <f:outputText/> i ustawiamy jego atrybut value na odpowiednie pole zmiennej osoba. <f:facet> tworzymy w celu dodania nagłówków dla tabeli.
W ostatniej kolumnie natomiast dodajemy przyciski commandButton i do komponentu dodajemy atrybut idPerson o wartości osoba.idPerson i tworzymy słuchacza akcji
<f:actionListener type="classes.MyDataModel" /> informując, że klasa MyDataModel implementuję odpowiednią metodę processAction() . Atrybut action wskazuje
na metodę MyDataModel.delete(), które de facto nic nie będzie robić tylko zwraca null :-).

Teraz po wytłumaczeniu komponentu h:dataTable trzeba zaimplementować odpowiednie metody aby wszystko działało poprawnie. Obie własności tableValues i idPerson powinny być private zamiast public ...
Widzimy metodę getTableValues, która wykonuje proste zapytanie hsql pobierające wszystkie osoby.





Teraz kod obsługujący zdarzenie kliknięcia:



57. Pobieram komponent, który wywołał akcję klikniecia.
58. Pobieram wartość atrybutu z znaczika commandButton o nazwie idPerson.
59-64. Kod odpowiada za usunięcie danej osoby.
65-69. Najważniejsze fragmenty kodu.

Teraz wytłumaczę dlaczego metoda redirect() jest tak ważna. Otóż gdy klikniemy przycisk usuń to formularz jest wysyłany na serwer, który nastepnie usuwa dany rekrod. Taka funkcjonalność nam odpowiada w 100%. Niestety odświeżenie strony (F5) powoduje ponowne wysłanie formularza na serwer. Formularz ten również niestety przez implementację JavaServer Faces zapisuje wszystkie dane żądania na serwerze i w fazie przywrócenia widoku przesyła je "spowrotem do przeglądarki". Gdy odświeżymy stronę atrybut idPerson zwiększa wartość o 1. Osobiście nie mam pojęcia dlaczego, ale podczas działania i debugowania zauważyłem tą własność.
Po każdej akcji kliknięcia wykonanie przekierowania na tę samą stronę skutkuje tym, że bieżące żądanie zostaje wstrzymane, a kontener JSP odsyła przekierowanie HTTP. Zatrzymanie bieżącego żądania skutkuje tym, że wszystkie dane formularza są po prostu tracone. Następnie gdy użytkownik ponownie odświeży stronę to formularz nie będzie zawierał parametru idPerson. Przy tej implementacji nie musimy martwić się o zdublowaną akcje usuwania rekordów. Druga zaleta takiego rozwiązania to, że po akcji redirect strona jest ponownie konstruowana i wywołana metoda getTableValues(). Dzięki temu nie musimy sami dbać o zmianę zawartości modelu ListDataModel i metod get/setWrappedData().