Integracja javy i groovy oraz wstęp do metaprogramowania

Po raz kolejny podwójna dawka wiedzy na podstawie książki Programming Groovy: Dynamic Productivity for the Java Developer. Na początek integracja kodu groovy z kodem java. Tak jak przypuszczałem podczas czytania powyższej książki sprawa jest prosta.

Są 2 opcje uruchomienia kodu groovy:
1.Przez polecenie groovy (automatyczne kompilowanie kodu w pamięci i wykonanie go).
2.Kompilacja przez groovyc i uruchomienie przez polecenie java (należy pamiętać o dodaniu biblioteki groovy do classpatha).

Zazwyczaj skrypty groovy nie są w żadnym pakiecie.

Nie ma różnicy między skompilowanym kodem groovy a java. Użycie klas groovy nie różni się niczym od użycia klas javowych. Jeśli klasy są skompilowane lub w jarze to ich użycie jest normalne (tak jak w javie).

Jeśli kod groovy jest skompilowany lub jarem od razu zadziała z kodem javowym. Jeśli to jest źródło należy użyć groovyc. Javac rozpoznaje wszystkie klasy od których zależy dana klasa i je kompiluje, ale nie rozpoznaje plików z rozszerzeniem groovy. Groovyc to potrafi i dla każdego pliku groovy potrafi skompilować pliki java, należy używać z flagą -j. (joint compilation – kompilacja łączona).

Jeśli wywoływany jest skrypt groovy w klasie groovy to należy użyć:

shell = new GroovyShell()
shell.evaluate(new File('Script1.groovy' ))

lub wersja skrócona

evaluate(new File('Script1.groovy' ))

Wywołanie skryptu groovy z poziomu javy odbywa się na podstawie JSR 223. Inne języki bardziej to wspierają niż groovy z powodu joint compilation, np:

import javax.script.*;
public class CallingScript{
  public static void main(String[] args) {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("groovy" );
    System.out.println("Calling script from Java" );
    try{
      engine.eval("println 'Hello from Groovy'" );
    }
    catch(ScriptException ex){
      System.out.println(ex);
    }
  }
}

Metaprogramowanie

Aby w pełni poznać plusy metaprogramowania w groovy należy zrozumieć obiekty w groovy i manipulowanie na metodach.
Obiekty groovy są co najmniej jak obiekty java ale posiadają więcej funkcji. Obiekty groovy mają bardziej dynamiczne zachowanie niż obiekty java, wywoływanie i operacje na metodach odbywają się w inny sposób niż w obiektach java.

W aplikacjach groovy pracuje się na 3 rodzajach obiektów:
-POJO – klasy javove
-POGO – klasy napisane w groovy rozszerzające java.lang.Object i implementujące groovy.lang.GroovyObject
-interceptory groovy – rozszerzające GroovyInterceptable

Dzięki metodom invokeMethod( ), getProperty( ), and setProperty( ) groovy jest dynamiczny.

Wszystkie interceptory i metody zdefiniowane w MetaClass klasy są ważniejsze niż oryginalne metody w POJO.
hasProperty() sprawdza czy istnieje właściwość.
respondsTo() sprawdza czy istnieje metoda.

Można wywołać metodę bez znania jej nazwy w trakcie pisani(dynamizm):

str = "hello"
methodName = 'toUpperCase'
// Name may come from an input instead of being hard coded
methodOfInterest = str.metaClass.getMetaMethod(methodName)
println methodOfInterest.invoke(str)

Dynamiczne wywołanie właściwości odbywa się w następujący sposób

obj[property]

lub

obj."$usrRequestedProperty"

a metody

obj."$usrRequestedMethod" ()

lub

obj.invokeMethod(usrRequestedMethod, null)

gdzie null to lista argumentów.

Usuwanie komunikatów systemowych

W jsf istnieje prosty sposób implementacji komunikatów w systemie. Czasami się zdarza, że pewna metoda tworzy w swoim ciele komunikat dla użytkownika a w innym miejscu wywołanie tej metody nie powinno wyświetlać żadnej informacji. Utworzenie takiej samej metody, tylko że bez komunikatu jest złamaniem zasady DRY , a znowu dodanie warunków, może doprowadzić do nadmiernej komplikacji.

Prostym rozwiązaniem jest zastosowanie poniższego kodu:

FacesContext context = FacesContext.getCurrentInstance();
   Iterator<String> clients = context.getClientIdsWithMessages();
   while (clients.hasNext()) {
     String clientId = clients.next();
     Iterator<FacesMessage> messages = context.getMessages(clientId);

     while (messages.hasNext()) {
        messages.next();
        messages.remove();
     }
   }

Gdzie są pobierane message a następnie podczas iteracji można je usunąć.

Kolorowanie składni dzięki tej stronie

HttpServletResponse and file name

It’s my first note in english. I think that their content could be better in english than in polish. So, forgive me any mistakes.

Sometimes in web application we create, we need to give download file functionality. Using HttpServletRespone is simple, maybe I’ll write about it soon. But I have problem with setting a file name. And it also was very easy. The solution of this problem is:

response.setHeader("Content-Disposition", "attachment; filename="+ fileName);

Inny sposób pobierania wiersza w jsf – 2

W poprzedniej notce podałem jeden sposób pobierania wierszy w jsf a dziś pora na drugi.

Do tabeli dodajemy nowy przycisk (można zastąpić ten, który już był).

<h:commandButton
   actionListener="#{myBean.doOtherAction}"
   action="done">
      <f:facet name="userId">
         <f:param name="name" value="#{user.id}"></f:param>
      </f:facet>
</h:commandButton>

Tutaj ciekawą rzeczą jest to, że definiujemy zamiast metody w akcji metodę w actionListener oraz facet “userId” do którego przypisujemy id użytkownika, który się znajduje w danym wierszu.

Metoda doOtherAction w beanie wygląda tak:
public void doOtherAction(ActionEvent actionEvent) {
   String userId = ((String) ((UIParameter) actionEvent.getComponent().getFacet("userId")).getValue());
//do sth
}

W taki sposób można pobrać wartość dla pola w obiekcie, które jest renderowane w tabeli.
Który z tych dwóch sposobów jest lepszy, trudno mi określić. Ja korzystam z nich naprzemiennie i jeśli potrzebuje całego obiektu, to wtedy korzystam z dataModelu a jeśli potrzebuje jedno pole wtedy z facetów.

pobieranie-wiersza-czesc-1

Napisane w jsf. 1 komentarz »

Pobieranie wiersza część 1

We wszystkich aplikacjach CRUD jedną z głównych czynności jest pobieranie konkretnego wiersza z listy i coś z nim robienie. Zwłaszcza, gdy ma się pewne złe nawyki z autorskiego web-frameworku (wiele rzeczy było w nim fajnych i w tym czasie gdy system powstawał to było duże ułatwienie zamiast czystego jsp i servletów), to na początku trudno się przestawić do sposobów pobierania wiersza w jsf.
Na szczęście jsf udostępnia prosty sposób pobierania wiersza z danej kolumny. Jak wiadomo znacznik tworzy tabelę na stronie iterując po wszystkich elementach listy, chociaż lepszy jest znacznik z rozszerzenia jsf – tomahawka. Tomahawk obsługuje więcej typów, między innymi Set.

<h:dataTable value='#{myBean.users}' var='user'>
 <h:column>
  <f:facet name="header">
    <h:outputText value="Name" />
   </f:facet>
  <h:outputText value="#{user.name}"/>
  </h:column>
  <h:column>
  <f:facet name="header">
    <h:outputText value="Surname" />
   </f:facet>
   <h:outputText value="#{user.surname}"/>
 </h:column>
  <h:column>
   <f:facet name="header">
    <h:outputText value="Action" />
  </f:facet>
   <h:commandButton value="Do sth" action="myBean.doSomething"/>
 </h:column>
<h:dataTable>

Jak na razie sprawa jest prosta. Pobieramy dane i iterujemy po nich tworząc tabelę z imieniem i nazwiskiem oraz jakimś przyciskiem. value=’#{myBean.users}’ odwołuje się do metody getUsers w beanie i przypisuje pojedynczy element listy do zmiennej user.

Nasza metoda może wyglądać tak:
private DataModel userModel;
//getters and setters
public DataModel getUsers() {
   UserService userService = new UserImpl();
   List<User> users= userService .getAllUsers();
   userModel = new ListDataModel(users);
   return userModel ;
}

A metoda doSomething tak:
public String doSomething(){
   User user = (User)userModel.getRow();
    //some action
   return "done";
}

Metoda pobierająca użytkowników wygląda całkiem zwyczajnie do tej linijki
userModel = new ListDataModel(users)
Co się tutaj dzieje i czym jest userModel a właściwie DataModel. DataModel jest abstrakcją, która na podstawie różnych źródeł danych, wspiera ich przetwarzanie w wierszach. Tutaj tworzymy nowy DataModel a właściwie ListDataModel z listy użytkowników, i zwracamy.
Dopiero cała magia dokonuje się w metodzie doSomething(), a dokładniej w linijce
User user = (User)userModel.getRow();
Metoda getRow() w automagiczny sposób zwraca nam wybrany obiekt, poprzez kliknięcie przycisku w wybranym wierszu a my tylko rzutujemy go na naszego Usera, i już można wykonać jakieś czynności na naszym użytkowniku.

inny-sposob-pobierania-wiersza-w-2

Porządki w faces-config

Pierwszy raz jak zobaczyłem w jaki sposób jest zorganizowana nawigacja w jsf bardzo mi się to spodobało. Moja radość z tego powodu trwała do momentu gdy pojawiło się trochę więcej stron, które korzystały w większości z tych samych reguł nawigacyjnych. Rozwiązując test na javablackbelt pojawiło mi się pytanie związane z tym problemem. Nie znałem odpowiedzi ale zaznaczyłem taką odpowiedź, którą bym chciał aby była prawdziwa. I tak się stało.


<navigation-rule>
  <from-view-id>*</from-view-id>>
  <navigation-case>
    <from-outcome>something</from-outcome>>
    <to-view-id>/page.jsp</to-view-id>>
  </navigation-case>
</navigation-rule>

Poprsostu genialne rozwiązanie. Gwiazdka zastępuje nam dowolną stronę czyli mamy globalną nawigację dla wszystkich stron, a pozostało tylko obsłużenie specyficznych zachowań strony.

A może ktoś zna sposób jak jeszscze można zrefaktoryzowac faces-config?

Załączanie stron w jsf

Chyba każdy z programistów zna zasadę, a conajmniej o niej słyszał, DRY (Don’t Repeat Yourself). Bardzo ogólnie polega ona na unikaniu powtórzeń kodu. W dobrze zaprojektowanych i utrzymywanych aplikacjach javowych nie jest trudno jej przestrzegać. Im program staje się bardziej skomplikowany, więcej osób go pisze tym bardziej przestrzeganie tej zasady staje się trudniejsze, ale to nie o tym jest ta bajka.

Trochę trudniejsza sprawa jest z tworzeniem stron jsp (a właściwie jsf, o czym będe pisał). O ile jeszcze jakieś statyczne elementy jak stopka czy nagłówek moża łatwo wyodrębnić, to może pojawić się problem z listami, tabelkami, formatkami, które są w wielu miejsach takie same bądź rożnią się nieznacznie od siebie. Chyba najlepszym rozwiązaniem jest includowanie części podstron do docelowej strony.

JSF w standardzie udostępnia nam kontrolkę . Próbowałem z niej korzystać i wszystko było dobrze do momentu aż CSS został dodany do strony. Załączana strona nie widziała arkuszu stylów. Kilka sposobów, które znalazłem niestety nie zapewniły mi powodzenia. Rozwiązaniem na którym się wzorowałem można znaleźć tutaj.
Tworzymy cześć naszej strony w odzielnym pliku i zapisujemy jako jsp. Taki plik może mieć w sobie załączone kolejne fragmenty stron. A następnie dołączamy taką stronę poprzez

<h:panelGroup>
 <jsp:include page="includedPage.jsp" />
</h:panelGroup>

To rozwiązanie sprawiło, że CSS był widoczny dla komponentów na dołączonej stronie oraz dodatkowo w kodzie było mniej śmieci niż subview generuje.
Ważna sprawą jest dołaczenie dyrektyw jsf do podstron(jeśli korzystają one z backing beanów), bo w przeciwnym razie można dostać wyjątek o niemożliwości sparsowania strony. Oraz uważać aby id komponentów się nie powtarzały.

Wymagany checkbox w jsf (required checkbox in jsf)

Kontrolki w jsf posiadają bardzo ciekawy atrybut a mianowicie “required”. Ustawienie jego na true zapewnia, że submit nie zostanie zatwierdzony dopóki, wymagane pole nie będzie uzupełnione. I samemu nie trzeba tego oprogramowywać.

Jednak dla wymaganych checkboxów pojawia się problem. Atrybut required sprawdza czy dany komponent nie jest nullem a nie zaznaczony checkbox ma wartość false. Która równocześnie jest wartością domyślną dla komponentów. Ale i z tym problemem można się uporać, pisząc własny walidator dla checkboxów w backing beanie.

Na stronie jsp dla checkboxa dodajemy wskazanie na metodę walidującą w beanie i ustawiamy atrybut required na true.
<h:selectBooleanCheckbox id="accept"
   validator="#{RegistryBean.validateCheckbox}"
   value="#{RegistryBean.accept}" required="true">
</h:selectBooleanCheckbox>

Natomiast metoda sprawdzająca czy checkbox jest wymagany wygląda następująco:
public void validateCheckbox(FacesContext context, UIComponent component, Object value) {
  if (value instanceof Boolean && ((Boolean) value).equals(Boolean.FALSE)) {
    FacesMessage message = new FacesMessage("You must choose checkbox");
    throw new ValidatorException(message);
  }
}

I tutaj walidator sprawdza czy nasz checkbox jest nie zaznaczony. Jeśli użytkownik nie zaznaczył checkboxa zostaje rzucony wyjątek ValidatorException, który pokazuje użytkownikowi, że dane pole jest wymagane za pomocą przyjaznego komunikatu.

Transformata xslt w javie

Dawno nie było nic o javie, to dziś pora na transformatę XSLT. Bardzo ciekawa rzecz, która pozwoli nam zwykłego xml przekształcić do postaci, która można pokazać zwykłemu użytkownikowi i on nie ucieknie. Nie wiem czy to będzie dobre porównanie ale pliki xsl są dla xml tym samym co css dla html.

Teraz przykład transformaty xslt w javie:

Result fin = new StreamResult(response.getWriter());

InputStream ins = new ByteArrayInputStream(content);
UnicodeInputStream uis = new UnicodeInputStream(ins, "UTF-8");

Source str = new StreamSource(new InputStreamReader(uis, "UTF-8"));

InputStream vins = new ByteArrayInputStream(visualization);
UnicodeInputStream vuis = new UnicodeInputStream(vins,"UTF-8");
Source xsl = new StreamSource(new InputStreamReader(vuis,"UTF-8"));

TransformerFactory transFact = TransformerFactory.newInstance();
Transformer trans = transFact.newTransformer(xsl);
trans.transform(str, fin);

I co my tu mamy. Content to zawartość naszego xml już w postaci bajtowej a visualization to xsl. Należy je przekształcić do typu Source aby można je było wykorzystać przy transformacie, którą wykonujemy na końcu i wynik mamy w fin.

Nie pamiętam bibliotek z których tutaj korzystałem ale jest kilka, które implementuje transformatę. Rzeczą na którą należy tu zwrócić uwagę jest klasa UnicodeInputStream. Ja korzystałem i lekko zmodyfikowałem tą klasę ale widziałem, że apache udostępnia własną klasę, która może być lepsza od tej przeze mnie używanej. Dzięki tej klasie możemy obejść problem z BOM. A czym jest BOM? Jest to kilka bajtów na początku pliku określających kodowanie. Niestety java go nie ignoruje i nie zanosi się na to aby zostało to rozwiązane. Tutaj z pomocą przychodzą na takie klasy jak UnicodeInputStream. Jak byśmy użyli standardowego InputStream to przy próbie transformaty otrzymalibyśmy błąd, że content is not allowed in prolog.

Logowanie sql w javie – log4jdbc

Zdecydowana większość pewnie słyszała o log4j, wiele osób też go używało i uważa za dobre narzędzie. Jednak co zrobić jak chcemy sprawdzić jakie operacje na bazie danych są wykonywane w naszej aplikacji a jeszcze lepiej ich czasy. Tutaj z pomocą przychodzi nam log4jdbc. Proste narzędzie, które pozwala nam logować informacje o zapytaniach, kwerendach i to dosyć szczegółowo.

W log4jdbc dostajemy 4 loggery:

  1. jdbc.sqlonly - najprostszy, pokazuje tylko zapytania SQL.
  2. jdbc.sqltiming - ukazane czasy wykonywania operacji, w większości przypadków ten jest wystarczający.
  3. jdbc.audit - wszystkie wywołania SQLowe oprócz ResultSet.
  4. jdbc.resultset - wszystko łącznie z ResulSet.

Całą konfigurację log4jdb możemy zdefiniować w pliku log4j.xml a tutaj znajduje się przykładowy plik log4j.xml z włączonym logowaniem zapytań do bazy danych.
Osobiście polecam takiego appendera

<appender name=”sql-timing-appender” class=”org.apache.log4j.RollingFileAppender”>
<param name=”File” value=”ścieżka_gdzie_zapisać_log/sql.log”/>
<param name=”Append” value=”true”/>
<param name=”Encoding” value=”UTF-8″/>
<param name=”MaxFileSize” value=”10000KB”/>
<layout class=”org.apache.log4j.PatternLayout”>
<param name=”ConversionPattern” value=”—–> %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n%n”/>
</layout>
</appender>

ponieważ możemy zdefiniować tutaj rozmiar logów a także plik za każdym razem będzie dopisywany do wcześniejszych logów a nie tworzony nowy.

Jak już mamy konfigurację zapisu to jeszcze potrzebujemy biblioteki. W pracy korzystałem z następujących (nie mogę tutaj wrzucić archiwów a na jakiś hostingu plików to zginie szybko, więc po nazwach będzie można je znaleźć) .

  • log4jdbc3-1.1beta
  • slf4j-api-1.4.3
  • slf4j-log4j12-1.4.3

Ostatnia rzeczą jaką wykonujemy jest zdefiniowanie połączenia z bazą danych na nowych ustawieniach. Zmieniamy sterownik na net.sf.log4jdbc.DriverSpy , który obsługuje najbardziej popularne bazy danych. Jeśli, którejś nam brak to zawsze możemy ustawić we właściwościach log4jdbc aby wskazywał na dany sterownik. A także zamieniamy stary adres lokalizacji bazy:
jdbc:derby://localhost:1527//db-derby-10.2.2.0-bin/databases/MyDatabase na
jdbc:log4jdbc:derby://localhost:1527//db-derby-10.2.2.0-bin/databases/MyDatabase


Po zakończeniu zbierania logów należy przywrócić poprzednie ustawienia połączenia z bazą, ponieważ na tym sterowniku baza danych chodzi dużo wolniej niż na natywnym, zaobserwowane na przykładzie sterownika posgresql