Java 7 w akcjiJava 7 in action Wreszcie znalazłem wolną chwilę by wypróbować w praktyce nowe ficzery Java 7. Zabawy z wersją beta najnowszego jdk ułatwia NetBeans IDE 7.0 Beta 2. Do testów na pierwszy ogień poszły usprawnienia...

Readmore

Monitoring podstawowych parametrów JVM z poziomu web... Problem Monitoring podstawowych parametrów JVM z poziomu web aplikacji - przydatne zwłaszcza wtedy, gdy nasz serwer aplikacji/kontener serwletów nie pokazuje takich informacji w swojej webowej konsoli...

Readmore

Vaadin vs Richfaces i o tym co z tego wyszło [Java... Głośno ostatnio na DWorld i DZone zrobiło się o nowej odsłonie Vaadina - frameworku opartego na GWT. Nigdy wcześniej nie miałem styczności z GWT (prócz kilku tutoriali i paru hellowordów). Pracuję...

Readmore

Jak wyciągnąć kilka pierwszych wyników zapytania... [sql] -- Oracle select a.* from (select rownum row_num, t.* from t_table t ) a where a.row_num <= N -- DB2 select * from t_table fetch first 10 rows only -- Informix select...

Readmore

Vademecum IBM i oraz darmowe konto na iSeriesIBM i... Znalazłem jakiś czas temu 'hosting' oparty o iSeries, na którym można założyć sobie darmowe konto. Gdyby ktoś zatem poczuł nieodpartą pokusę pobawienia się AS/400 Green Screen, to ma taką...

Readmore

twitter

Java 7 w akcji

Kategoria : java

Wreszcie znalazłem wolną chwilę by wypróbować w praktyce nowe ficzery Java 7.

Zabawy z wersją beta najnowszego jdk ułatwia NetBeans IDE 7.0 Beta 2. Do testów na pierwszy ogień poszły usprawnienia w samej składni języka.

Jak widać na załączonym obrazku ‚Project Coin is alive':

Po więcej informacji odsyłam do:
http://openjdk.java.net/projects/jdk7/features/
http://www.java7developer.com/

Wracam do dalszego rozpoznawania nowych zabawek ;)

Monitoring podstawowych parametrów JVM z poziomu web aplikacji

Kategoria : java

Problem

Monitoring podstawowych parametrów JVM z poziomu web aplikacji – przydatne zwłaszcza wtedy, gdy nasz serwer aplikacji/kontener serwletów nie pokazuje takich informacji w swojej webowej konsoli i nie mamy możliwości szybkiego podpięcia się VisualVM albo JConsole (brak aktywowanego jmxremote, firewalle, czy cokolwiek innego uniemożliwiającego połączenie), a podejrzewamy że coś niedobrego może dziać się z naszą aplikacją.

Rozwiązanie

Wrzucenie prostej stronki JSP, która nam takie parametry wyświetli.

Poniżej listing (po moich drobnych modyfikacjach) zapożyczony z http://www.freshblurbs.com/explaining-java-lang-outofmemoryerror-permgen-space

Skryptlet (tak wiem, śmierdzi) użyty na tej stronie pozwala wrzucać ją do aplikacji bez potrzeby restartu serwera/redeploy aplikacji.

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ page import="java.util.*, java.lang.management.*" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    	               "http://www.w3.org/TR/html4/loose.dtd">

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>JVM Memory Monitor</title>
    <style type="text/css">
      td {
        text-align: right;
      }
    </style>
  </head>
<body>
  <jsp:scriptlet>
    MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
    pageContext.setAttribute("memoryBean", memoryBean, PageContext.PAGE_SCOPE);

    List poolBeans = ManagementFactory.getMemoryPoolMXBeans();
    pageContext.setAttribute("poolBeans", poolBeans, PageContext.PAGE_SCOPE);
  </jsp:scriptlet>

  <h3>Total Memory</h3>
  <table border="1" width="100%">
    <tr>
      <th>usage</th>
      <th>init</th>
      <th>used</th>
      <th>committed</th>
      <th>max</th>
    </tr>
    <tr>
      <td style="text-align: left">Heap Memory Usage</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.heapMemoryUsage.init / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.heapMemoryUsage.used / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.heapMemoryUsage.committed / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.heapMemoryUsage.max / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
    </tr>
    <tr>
      <td style="text-align: left">Non-heap Memory Usage</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.nonHeapMemoryUsage.init / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.nonHeapMemoryUsage.used / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.nonHeapMemoryUsage.committed / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.nonHeapMemoryUsage.max / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
    </tr>
  </table>

  <h3>Memory Pools</h3>
  <table border="1" width="100%">
    <tr>
      <th>Name</th>
      <th>Usage</th>
      <th>Init</th>
      <th>Used</th>
      <th>Committed</th>
      <th>Max</th>
    </tr>
    <c:forEach var="bean" items="${pageScope.poolBeans}">
      <tr>
        <td style="text-align: left"><c:out value="${bean.name}" /></td>
        <td style="text-align: left">Memory Usage</td>
        <td><fmt:formatNumber value="${bean.usage.init / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
        <td><fmt:formatNumber value="${bean.usage.used / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
        <td><fmt:formatNumber value="${bean.usage.committed / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
        <td><fmt:formatNumber value="${bean.usage.max / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      </tr>
      <tr>
        <td></td>
        <td style="text-align: left">Peak Usage</td>
        <td><fmt:formatNumber value="${bean.peakUsage.init / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
        <td><fmt:formatNumber value="${bean.peakUsage.used / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
        <td><fmt:formatNumber value="${bean.peakUsage.committed / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
        <td><fmt:formatNumber value="${bean.peakUsage.max / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      </tr>
    </c:forEach>
  </table>
</body>
</html>

Efekt poniżej:

To rozwiązanie tak mi się spodobało, że z rozpędu napisałem sobie to samo w Richfaces. Strona odświeża się automatycznie co zadany przedział czasu (w ms). Wykorzystanie Ajax Push/Poll nie powala na pewno z punktu widzenia wydajności, jest natomiast rozwiązaniem banalnym do zaimplementowania:

<html xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:a4j="http://richfaces.org/a4j"
    xmlns:rich="http://richfaces.org/rich">

<head>
    <title>JVM Memory Monitor</title>
</head>

<body>
    <a4j:region>
        <h:form>
            <a4j:poll id="poll" interval="#{jvmPerformanceManager.interval}" enabled="#{jvmPerformanceManager.pollEnabled}" reRender="polledPanel, poll"/>
        </h:form>
    </a4j:region>
    <h:form prependId="false">
        <h:panelGrid id="polledPanel" columns="1">
            <h3>Total Memory</h3>
            <rich:dataTable value="2" style="text-align: right">
                <f:facet name="header">
                    <rich:columnGroup>
                        <rich:column>
                            <h:outputText value="Name" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Init" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Used" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Committed" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Max" />
                        </rich:column>
                    </rich:columnGroup>
                </f:facet>
                <rich:column>
                    <h:outputText value="Heap Memory Usage" />
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.heapMemoryUsage.init / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.heapMemoryUsage.used / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.heapMemoryUsage.committed / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.heapMemoryUsage.max / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column breakBefore="true">
                    <h:outputText value="Non-heap Memory Usage" />
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.nonHeapMemoryUsage.init / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.nonHeapMemoryUsage.used / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.nonHeapMemoryUsage.committed / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.nonHeapMemoryUsage.max / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>

                <f:facet name="footer">
                    <rich:columnGroup>
                        <rich:column>Last actualization date</rich:column>
                        <rich:column colspan="4">
                            <h:outputText value="#{jvmPerformanceManager.lastActualization}">
                                <f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss" timeZone="#{jvmPerformanceManager.timeZone}"/>
                            </h:outputText>
                        </rich:column>
                    </rich:columnGroup>
                </f:facet>
            </rich:dataTable>

            <h3>Memory Pools</h3>
            <rich:dataTable var="poolBean" value="#{jvmPerformanceManager.poolList}"
                style="text-align: right">
                <f:facet name="header">
                    <rich:columnGroup>
                        <rich:column>
                            <h:outputText value="Name" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Usage" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Init" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Used" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Committed" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Max" />
                        </rich:column>
                    </rich:columnGroup>
                </f:facet>
                <rich:column rowspan="2">
                    <h:outputText value="#{poolBean.name}" />
                </rich:column>
                <rich:column>
                    <h:outputText value="Memory" />
                </rich:column>
                <rich:column>
                    <h:outputText value="#{poolBean.usage.init / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText value="#{poolBean.usage.used / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText value="#{poolBean.usage.committed / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText value="#{poolBean.usage.max / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column breakBefore="true">
                    <h:outputText value="Peak" />
                </rich:column>
                <rich:column>
                    <h:outputText value="#{poolBean.peakUsage.init / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText value="#{poolBean.peakUsage.used / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{poolBean.peakUsage.committed / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText value="#{poolBean.peakUsage.max / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
            </rich:dataTable>

            <h:panelGrid columns="2">
                <h:column>
                    <a4j:commandButton id="controlBtn"
                        value="#{jvmPerformanceManager.pollEnabled?'Stop':'Start'}" reRender="polledPanel, poll">
                    <a4j:actionparam name="polling" value="#{!jvmPerformanceManager.pollEnabled}"
                        assignTo="#{jvmPerformanceManager.pollEnabled}" />
                    </a4j:commandButton>
                </h:column>
                <h:column>
                    <a4j:outputPanel style="display: inline" rendered="#{!jvmPerformanceManager.pollEnabled}">
                        <h:outputText value=" with " />
                        <h:inputText value="#{jvmPerformanceManager.interval}">
                            <f:validateLongRange minimum="500" />
                        </h:inputText>
                        <h:outputText value=" ms interval." />
                    </a4j:outputPanel>
                </h:column>
            </h:panelGrid>
        </h:panelGrid>
    </h:form>
</body>
</html>
package info.ludera.helper;

import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.event.ActionEvent;

@ManagedBean(name="jvmPerformanceManager")
@SessionScoped
public class JvmPerformanceManager implements Serializable {

    private MemoryMXBean memoryBean;

    private List<MemoryPoolMXBean> poolList;

    private Boolean pollEnabled = true;

    /**
     * Interval in milliseconds. Default 10s.
     */
    private Long interval = 10*1000l;

    private String timeZone;

    public JvmPerformanceManager() {
        getActualMemoryInfo(null);
        timeZone = readDefaultHostTimeZone();
    }

    private String readDefaultHostTimeZone() {
        return TimeZone.getDefault().getID();
    }

    public void getActualMemoryInfo(ActionEvent actionEvent) {
        memoryBean = ManagementFactory.getMemoryMXBean();
        poolList = ManagementFactory.getMemoryPoolMXBeans();
    }

    public MemoryMXBean getMemoryBean() {
        return memoryBean;
    }

    public List<MemoryPoolMXBean> getPoolList() {
        return poolList;
    }

    public Boolean getPollEnabled() {
        return pollEnabled;
    }

    public void setPollEnabled(Boolean pollEnabled) {
        this.pollEnabled = pollEnabled;
    }

    public Long getInterval() {
        return interval;
    }

    public void setInterval(Long interval) {
        this.interval = interval;
    }

    public Date getLastActualization() {
        return Calendar.getInstance().getTime();
    }

    public String getTimeZone() {
        return timeZone;
    }
}

Efekt:

Jestem obecnie, jak pewnie wielu, na etapie rozpoznawania nowości w Java EE 6. Treningowo, postanowiłem zatem napisać ten sam JVM Monitor z wykorzystaniem Asynchronious Processing Support z Servlets 3.0 – także wkrótce rozwinięcie tematu.

Vaadin vs Richfaces i o tym co z tego wyszło [Java EE 6, Maven, Glassfish, Oracle]

Kategoria : java

Głośno ostatnio na DWorld i DZone zrobiło się o nowej odsłonie Vaadina – frameworku opartego na GWT. Nigdy wcześniej nie miałem styczności z GWT (prócz kilku tutoriali i paru hellowordów). Pracuję natomiast na codzień z JSF – najczęściej w implementacji Richfaces. Chciałem więc sprawdzić co potrafi ten rozchwalany na lewo i prawo Vaadin oraz jak jego zestaw gotowych komponentów ma się do Richfaces w kontekście budowania interfejsu użytkownika dla aplikacji ‚biznesowych’ (tabele, modalpanele, itp, itd) w konwencji podbierania gotowych komponentów ze stron frameworków (odpowiednio tej i tej). Mam świadomość tego, że porównywanie Vaadina do Richfaces to jak zestawianie arbuzów z dyniami – za jedyne kryterium porównawcze chciałem zatem przyjąć ogólną prostotę danego rozwiązania. Chodziło o to żeby zrobić (CRUDa, a jakże) a się nie narobić ;)

Najprostszą metodą stworzenia działającego projektu Vaadin, jest wyklikanie go z poziomu Eclipse (kreatory i edytor WYSIWYG). Najłatwiejszą, jaką znam, metodą zbudowania projektu opartego o Richfaces jest pobranie przykładowej aplikacji i wyrzucenie niepotrzebnych plików albo… wygenerowanie Seamowego projektu seam-gen’em. Można również oczywiście w obu przypadkach bawić się w Mavena.

Poznawanie Vaadina rozpocząłem właśnie od napisania sobie AddressBook‚a – nic innego jak CRUD, który chciałem stworzyć samemu… Stwierdziłem więc, że może chociaż samemu dorzucę ORM (JPA) i zobaczę czy nadal wszystko będzie tak gładko chodzić, to też już ktoś wcześniej zrobił ;) Pomyślałem więc, że zbiorę chociaż wszystko to do kupy i sprawdzę czy się nie ‚gryzie’. Napisałem zatem mavenowy AddressBook z JPA, pracujący na Glassfish 3.1 z Oracle 10g Express Edition w tle – zadziałało. Poniżej listing jak wygenerować szablon projektu Vaadina w mavenie, oraz gotowy projekt do pobrania plus kilka podpowiedzi jak coś takiego uruchomić tudzież ‚napisać’ samemu.

0) instalujemy i konfigurujemy Oracle 10g Express Edition

1) pobieramy najnowszego glassfisha 3.1 (wersja 3 zawiera bug niepozwalający na swobodne korzystanie z Vaadin – chodzi o ładowanie klas)

2) do $GLASSFISH_HOME/glassfish/lib dorzucamy sterownik ojdbc14.jar

3) z $GLASSFISH_HOME/bin uruchamiamy asadmin start domain – startujemy domyślną (domain1) domenę serwera wszystkorobiącym skryptem asadmin

4) otwieramy konsolę administracyjną Glassfisha (http://localhost:4848) i wybieramy z menu Resources->JDBC->JDBC Connection Pools

5) dodajemy pulę połączeń do naszej bazy

6) dodajemy JDBC Recource (o nazwie jdbc/sample) dla skonfigurowanej przed chwilą puli połączeń – będziemy się do tego źródła danych odwoływać z poziomu aplikacji (w persistance.xml)

7) restartujemy serwer (asadmin stop-domain; asadmin start-domain)

8 ) generujemy projekt skryptem mvnAddressBook.sh:

#!/bin/bash
if [ "$#" -gt 2 ]
then
	groupId=$1
	artifactId=$2
else
	artifactId=AddressBook
	groupId=com.vaadin.demo.tutorial.addressbook
fi
mvn archetype:generate -DarchetypeGroupId=com.vaadin -DarchetypeArtifactId=vaadin-archetype-clean -DarchetypeVersion=LATEST -DgroupId="$groupId" -DartifactId="$artifactId" -Dversion=1.0 -Dpackaging=war
cd $artifactId; mvn eclipse:eclipse

9) importujemy projekt do Eclipse z zainstalowanym pluginem do Vaadina (dzięki temu będziemy mieć edytor WYSIWYG do składania GUI z klocków)

10) rozwiązanie zależności z Glassfish w pom.xml (po pobraniu projektu dołączonego do tego wpisu ustaw własną ścieżkę!)

11) teraz najważniejsze. według uznania, przechodzimy przez tutorial AddressBook i AddressBook+JPA pamiętając o hierarchii projektu narzuconej przez Maven lub ściągamy gotowy projekt i przerzucamy kod do naszego. Dodajemy również web.xml z pustą deklaracją – korzystamy z Java EE6, więc konfigurujemy serwlety adnotacjami – patrz AddressBookApplication (jak widać konfiguracja Vaadina jest właściwie zerowa,w odróżnieniu od JSF gdzie skazani jesteśmy na klepanie kilometrowych plików typu faces-config.xml)

12) dorzucamy katalogi src/test/resources i implementujemy testy jednostkowe! ;)

13) mvn compile war:war

14) delpoy aplikacji z poziomu konsoli administracyjnej Glassfish (pozycja menu Applications), z Eclipsa lub Mavenem.

Podsumowanie.

Nie pisałem ostatecznie analogicznej aplikacji w Seam (z Richfaces). Wiem bowiem jak coś takiego zrobić i jak świetnym narzędziem do generowania CRUDów jest seam-gen. Jeśli chodzi o Vaadin to muszę powiedzieć, że ten framework przegania pod względem prostoty JSF (nawet z tak świetnym zestawem komponentów jak Richfaces). Pisząc w Vaadin ani razu nie musimy zaglądać do web.xml, czy innych charakterystycznych dla frameworku xmli – i to jest świetne. Kuleje mocno edytor WYSIWYG – obecnie w fazie testów. Jak widać Vaadin da się zaprząc całokowicie bezboleśnie do współpracy z Java EE. Ciekaw natomiast jestem, czy da się (i czy jest sens) pożenić Vaadina z Seamem – ktoś próbował?

Wnioski.

Koniecznie zmienić skórkę dla bloga, bo się nie mieszczę!

PS. Jeszcze taka jedna obserwacja. Dzięki temu, że JSF podzielony jest na widok (jsp lub facelets) i kontroler (managed beans), ciężej niż w Vaadin jest napisać śmietnik zamiast kodu. Jedyne ryzyko, to to że nasze beany zamiast obiektowych będą kodem proceduralnym. Vaadin jest badzo podobny do Swinga, wypada więc strategicznie komponować kod zamiast uwailić całe GUI w metodzie init() ;) Sugestie jak pisać czysty kod w Vaadin znaleźć można w samej aplikacji AddressBook oraz w Book of Vaadin (nie mylić z tym) ;)

PS2. Zapowiada się świetna okazja, by już 20.04.2010 posłuchać paru mądrych słów o Vaadin. Ja z przyczyn obiektywnych niestety nie będę mógł skorzystać, niemniej jednak zapraszam bo na pewno warto!

Kompletny projekt do pobrania

Jak wyciągnąć kilka pierwszych wyników zapytania SQL

Kategoria : it

-- Oracle
select a.*
  from (select rownum row_num, t.*
          from t_table t
       ) a
 where a.row_num <= N

-- DB2
select *
  from t_table
 fetch first 10 rows only

-- Informix
select first 10 *
  from t_table

-- PostgreSQL
select *
  from table limit 10

-- MS SQL
select top 10 *
  from table

-- MySQL
select *
  from table limit 10

Osobną sprawą jest stworzenie okna przydatnego do stronnicowania wyników zapytania. Wówczas składnia Oracle jest wg mnie najbardziej intuicyjna – używamy zwykłego ANSI SQL (beetween, operatory >< itp).

Inne bazy korzystają najczęściej z różnych wariacji słowa kluczowego offset lub limit z dwoma parametrami.

Vademecum IBM i oraz darmowe konto na iSeries

Kategoria : ibm i

ibm-i-for-dummies

Znalazłem jakiś czas temu ‚hosting’ oparty o iSeries, na którym można założyć sobie darmowe konto. Gdyby ktoś zatem poczuł nieodpartą pokusę pobawienia się AS/400 Green Screen, to ma taką możliwość.

Pod tym adresem, znajduje się natomiast niezła ściąga z obsługi OS/400. Czytaj dalej

Zabezpieczony: Analiza pakietu programistycznego środowiska iSeries

Kategoria : ibm i

Treść jest chroniona. Proszę podać hasło:

Singleton z double-checked locking oraz problem z wielowątkowością w TestNG

Kategoria : java

Wyczytałem ostatnio w mądrych książkach (tej i tej), że synchronizacja singletonów na poziomie całej metody getInstance może w środowisku wielowątkowym znacznie (25%) spowolnić pobieranie instancji obiektu przechowywanego przez owy singleton. W polskiej wikipedii ktoś napisał, że synchronizacja ta może obniżyć wydajność (w stosunku do metody niesynchronizowanej) o czynnik 100 lub więcej – szczerze, nie rozumiem co oznacza rzeczony ‚czynnik 100’…

W przypadku, gdy wydajność ma dla nas kluczowe znaczenie, do tworzenia singletonów zalecano stosowanie wzorca double-checked locking (blokada z podwójnym zatwierdzaniem). Postanowiłem więc sprawdzić doświadczalnie jak w rzeczywistości wygląda spadek wydajności metod synchronizowanych. Napisałem więc trzy singletony:

tradycyjny

package info.ludera.SingletonPerformance.action.impl;

import info.ludera.SingletonPerformance.action.TestSingleton;

/**
 * Singleton z ZAWSZE synchronizowaną metodą {@link SimpleSingleton#getInstance()}
 * @author darekl
 *
 */
public class SimpleSingleton extends TestSingleton {

	/**
	 * Jedyna instancja obiektu {@link SimpleSingleton}
	 */
	private static SimpleSingleton instance;

	/**
	 * Konstruktor domyślny
	 */
	private SimpleSingleton() {
		super();
	}

	/**
	 * Zwraca instancję obiektu  {@link SimpleSingleton}
	 * @return
	 */
	public static synchronized SimpleSingleton getInstance() {

		if (instance == null) {
			instance = new SimpleSingleton();
		}
		return instance;
	}
}

i dwie różne implementacje double-checked locking:

„podstawowa”

package info.ludera.SingletonPerformance.action.impl;

import info.ludera.SingletonPerformance.action.TestSingleton;

/**
 * Singleton z synchronizowaną metodą {@link SimpleSingleton#getInstance()}. Synchronizacja aktywna jest tylko przy pierwszym uruchomieniu tej metody.
 * @author darekl
 *
 */
public class ThreadSaveSingleton extends TestSingleton {

	/**
	 * Jedyna instancja obiektu {@link SimpleSingleton}
	 */
	private volatile static ThreadSaveSingleton instance = null;

	/**
	 * Konstruktor domyślny
	 */
	private ThreadSaveSingleton() {
		super();
	}

	/**
	 * Zwraca instancję obiektu  {@link SimpleSingleton}
	 * @return
	 */
	public static ThreadSaveSingleton getInstance() {

		if (instance == null) {
			synchronized (ThreadSaveSingleton.class) {
				if (instance == null) {
					instance = new ThreadSaveSingleton();
				}
			}
		}
		return instance;
	}
}

i „rozszerzona

package info.ludera.SingletonPerformance.action.impl;

import info.ludera.SingletonPerformance.action.TestSingleton;

/**
 * Singleton z synchronizowaną metodą {@link SimpleSingleton#getInstance()}. Synchronizacja aktywna jest tylko przy pierwszym uruchomieniu tej metody.
 * @author darekl
 *
 */
public class AdvancedThreadSaveSingleton extends TestSingleton {

	/**
	 * Jedyna instancja obiektu {@link SimpleSingleton}
	 */
	private volatile static AdvancedThreadSaveSingleton instance = null;

	/**
	 * Konstruktor domyślny
	 */
	private AdvancedThreadSaveSingleton() {
		super();
	}

	/**
	 * Zwraca instancję obiektu  {@link SimpleSingleton}
	 * @return
	 */
	public static AdvancedThreadSaveSingleton getInstance() {

		AdvancedThreadSaveSingleton result = instance;
		if (result == null) {
			synchronized (AdvancedThreadSaveSingleton.class) {
				result = instance;
				if (result == null) {
					instance = result = new AdvancedThreadSaveSingleton();
				}
			}
		}
		return result;
	}
}

Przetestowałem ich działanie poniższym testem dla różnej ilości wątków (100-100000):

package info.ludera.SingletonPerformance.test;

...

public class FabricMultiThreadSingletonTest extends AbstractSingletonTest {

	@DataProvider
	public Object[][] ValidJavaVersion() {

		return new Object[][]{
				{ 5 }
		};
	}

	/**
	 * Ilość iteracji
	 */
	protected static Long testCounter;

	/**
	 * Prefix nazwy wątku
	 */
	protected static String threadNamePrefix = "Thread_";

	@BeforeClass
	@Parameters(value="repetitionQuantity")
	public void setTestCounter(long repetitionQuantity) {
		testCounter = repetitionQuantity;
	}

	@Test
	public void getSimpleSingletonNTimes() {

		for (long i=0; i<testCounter; i++) {
			SingletonFactory simpleSingletonFactory = new SimpleSingletonFactory(threadNamePrefix + i);
			simpleSingletonFactory.start();
		}
	}

	@Test(dataProvider = "ValidJavaVersion")
	public void getThreadSaveSingletonNTimes(final int javaVersion) {

		//Assert.assertTrue((Integer.parseInt("" + System.getProperty("java.version").charAt(2)) >= javaVersion), "This test is avialable only with Java 5 or above!");

		for (long i=0; i<testCounter; i++) {
			SingletonFactory threadSaveSingletonFactory = new ThreadSaveSingletonFactory(threadNamePrefix + i);
			threadSaveSingletonFactory.start();
		}
	}

	@Test(dataProvider = "ValidJavaVersion")
	public void getAdvencedThreadSaveSingletonNTimes(final int javaVersion) {

		//Assert.assertTrue((Integer.parseInt("" + System.getProperty("java.version").charAt(2)) >= javaVersion), "This test is avialable only with Java 5 or above!");

		for (long i=0; i<testCounter; i++) {
			SingletonFactory advancedThreadSaveSingletonFactory = new AdvancedThreadSaveSingletonFactory(threadNamePrefix + i);
			advancedThreadSaveSingletonFactory.start();
		}
	}
}

I rzeczywiście. Czasowe wyniki działania tych testów, dla różnej ilości iteracji, rozchodziły się w słuszną stronę. Wraz ze wzrostem iteracji, SimpleSingleton działał coraz wolniej w stosunku do wersji z double-checked locking. Testy przeprowadzałem na JDK 1.6.0_16 oraz JDK 1.5.0_22. Nie testowałem wydajności na Java 1.4. Istnieje bowiem prawdopodobieństwo wystąpienia problemu błędnej implementacji volatile. Oto przykładowe wyniki dla 10000 iteracji na JDK 1.6:

getSimpleSingletonNTimes - 1624ms
getThreadSaveSingletonNTimes - 1209ms
getAdvencedThreadSaveSingletonNTimes - 1153ms

To nie wszystko. Po zastanowieniu, doszedłem do wniosku, że użycie wzorca fabryki do takich teścików to przerost formy nad treścią. Stwierdziłem więc, że wykorzystam wielowątkowości z TestNG.

oto test:

package info.ludera.SingletonPerformance.test;

...

import org.testng.annotations.Test;

/**
 * Test wydajności synchronizacji singletonów {@link info.ludera.SingletonPerformance.test.helper.SimpleSingletonFactory} i {@link info.ludera.SingletonPerformance.test.helper.ThreadSaveSingletonFactory}
 * @author darekl
 *
 */
public class MultiThreadSingletonTest extends AbstractSingletonTest {

	@Test(threadPoolSize=1000, invocationCount = 1000)
	public void getSimpleSingleton() {

		SimpleSingleton.getInstance();
	}

	@Test(dataProvider = "ValidJavaVersion", threadPoolSize=1000, invocationCount = 1000)
	public void getThreadSaveSingleton(final int javaVersion) {

		//Assert.assertTrue((Integer.parseInt("" + System.getProperty("java.version").charAt(2)) >= javaVersion), "This test is avialable only with Java 5 or above!");

		ThreadSaveSingleton.getInstance();
	}

	@Test(dataProvider = "ValidJavaVersion", threadPoolSize=1000, invocationCount = 1000)
	public void getAdvencedThreadSaveSingleton(final int javaVersion) {

		//Assert.assertTrue((Integer.parseInt("" + System.getProperty("java.version").charAt(2)) >= javaVersion), "This test is avialable only with Java 5 or above!");

		AdvancedThreadSaveSingleton.getInstance();
	}
}

i wyniki (tym razem dla 1000 wątków):

getSimpleSingletonNTimes - 110ms
getThreadSaveSingletonNTimes - 97ms
getAdvencedThreadSaveSingletonNTimes - 100ms
getSimpleSingleton - 716ms
getThreadSaveSingleton - 2050ms
getAdvencedThreadSaveSingleton - 2079ms

Zastanawiające. Dlaczego trzy ostatnie testy (te dopisane powyżej) nie układają się tak jak te poprzednie, gdzie wielowątkowość napisałem ręcznie? Dlaczego widać tak duże różnice w wydajności? Przyznam szczerze, że z wielowątkowości w TestNG korzystam po praz pierwszy, czy ktoś bardziej doświadczony w tym temacie, mógłby mi wyjaśnić w czym tkwi problem na który się natknąłem i co robię źle?

Pobierz projekt eclipse (wymagania: Maven2 lub Eclipse z pluginami: m2eclipse i TestNG)

PS. Apropos optymalizacji kodu pod względem jego wydajności, polecam ten artykuł. Po jego przeczytaniu nasunęła mi się analogia dotycząca RDBMS i hintów oraz przesiadki z optymalizatorów regułowych na kosztowe ;)

package info.ludera.SingletonPerformance.action.impl;

import info.ludera.SingletonPerformance.action.TestSingleton;

/**
* Singleton z ZAWSZE synchronizowaną metodą {@link SimpleSingleton#getInstance()}
* @author darekl
*
*/
public class SimpleSingleton extends TestSingleton {

/**
* Jedyna instancja obiektu {@link SimpleSingleton}
*/
private static SimpleSingleton instance;

/**
* Konstruktor domyślny
*/
private SimpleSingleton() {
super();
}

/**
* Zwraca instancję obiektu {@link SimpleSingleton}
* @return
*/
public static synchronized SimpleSingleton getInstance() {

//System.out.println(„SimpleSingleton.getInstance start for ” + Thread.currentThread().getName());
if (instance == null) {
instance = new SimpleSingleton();
}
//System.out.println(„SimpleSingleton.getInstance stop for ” + Thread.currentThread().getName());
return instance;
}
}

Alior Bank assistance

Kategoria : live

Miałem przed chwilą okazję przetestować w działaniu pakiet Pomocni Fachowcy, który Alior Bank z Mondial Assistance zaproponował ostatnio swoim klientom. Potrzebowałem mianowicie pomocy drogowej. Poniżej przebieg akcji ‚reanimacja-mojego-auta':

6.30 – telefon na jak się okazało przeznaczoną specjalnie dla klientów Aliora Infolinię Assistance (tel. 0048 22 563 11 15). O dziwo nie zaspana Pani odebrała ode mnie podstawowe dane i poinformowała, że po mojej autentykacji i autoryzacji do skorzystania z tego pakietu do mnie ‚czemprędzej’ oddzwoni.

6.35 – obiecany telefon od Pani (plus gratisowa walidacja czy nie pomyliłem swojego numeru telefonu podczas poprzedniej rozmowy :) ). Powiedziałem co się stało, gdzie jestem itp. Zostałem poinformowany, że pomoc drogowa powinna się ze mną wkrótce skontaktować.

6.45 – telefon od pomocy drogowej, ustalenie znaków rozpoznawczych itp itd. Pomoc będzie za godzinę bo są korki.

7.40 – telefon z pomocy drogowej, żebym ruszył cztery litery na parking bo już są.

7.42 – 8.00 – kilka uderzeń młotkiem, parę bujnięć samochodem, kilka mocnych szarpnięć za drążek do hamulca ręcznego – wszystko jak się okazało skutecznie. Co fachura to fachura ;)

8.00 – la grande finale. Zero papierków, uścisk dłoni i po sprawie.

PS. Piszę to, bo nigdy w życiu nie korzystałem z takich ‚darmowych bonusów do różnych produktów na czarną godzinę’ i sądziłem, że jeśli coś jest za darmo to i jakość jest… średnia. Jak się okazało firma Mondial i partnerzy w terenie dają radę! Do niemiłego następnego.

JSF – automatyczna zamiana null na 0 lub pusty string

Kategoria : java

Myślalem, że JSF nie ma już przede mną większych tajemnic, gdy w projekcie objawił się nam następujący błąd:

JSF domyślnie z automatu zamienia formularzowe puste inputy na odpowiadające im 0 lub puste stringi. Inaczej mówiąc jeśli użytkownik do takiego inputTextu nic nie wpisze to referencja age zamiast być pusta (null) wskazywać będzie na 0.

<h:inputText id="age"
value="#{exampleManager.age}" converter="IntegerConverter" />

Gdyby age było Stringiem, wówczas JSF przypisałby pod niego „”.

Rozwiązanie tego ‚problemu’ okazało się trywialne choć dziwne (stąd ta notka). Należało zmienić domyślną wartość flagi Tomcata COERCE_TO_ZERO poprzez ustawienie odpowiedniego parametru uruchomieniowego:

-Dorg.apache.el.parser.COERCE_TO_ZERO=false

Domyślna wartość COERCE_TO_ZERO ustawiona jest na true. Oczywiście można tego dokonać również z poziomu kodu aplikacji – jak dla mnie rozwiązanie takie jest dużo bardziej elastyczne i eleganckie:

System.getProperties().
put("org.apache.el.parser.COERCE_TO_ZERO", "false");

Projekt w którym objawił się ten problem pracuje na JBossie (posiadającym w sobie Tomcata), jak aplikacja ta zachowała by się na innych serwerach aplikacyjnych i jak wówczas poradzić sobie z opisywanym ‚wymuszaniem’ – kiedyś może się dowiem…

mgr inż. Dariusz Ludera …

Kategoria : Cv

…brzmi dumnie :-) 16 września 2009 udało mi się obronić moją pracę magisterską i jednocześnie uzyskać tytuł zawodowy magistra inżyniera. Tematem mojej pracy mgr była Analiza pakietu programistycznego środowiska iSeries (System i Development Environment Analysis).

W ramach niniejszej pracy poddałem analizie dostępne na rynku narzędzia programistyczne na platformę IBM i. Oprócz opisu technologii programistycznych możliwych do wykorzystania na tejże platformie, skupiłem się na dokonaniu charakterystyki narzędzi wspomagających pracę przy tworzeniu nowych i rozwijaniu wcześniej stworzonych aplikacji. Praca miała na celu przeanalizowanie zakresu wykorzystania poszczególnych narzędzi, które mogą być używane w procesie wytwarzania aplikacji oraz odpowiedzenie na pytanie czy technologie i narzędzia stworzone kilka dekad temu nadają się do wykorzystania podczas budowania systemów zgodnych z dzisiejszymi standardami (głównie z podejściem SOA). Analizie poddano podejście firmy IBM do rozwoju swoich narzędzi i środowisk programistycznych ułatwiających pracę programistów związanych z tworzeniem aplikacji zarówno w nowych, jak i starych technologiach. Przy opisie poszczególnych technologii, dokonałem oceny stosowalności każdej z nich w aplikacjach tworzonych obecnie na potrzeby sektora finansowego i bankowego. W końcowej części niniejszej pracy pokusiłem się o znalezienie optymalnego narzędzia dla programistów i podanie zestawu najlepszych moim zdaniem technologii, które połączone ze sobą, stanowić będą podstawę do tworzenia oprogramowania dla wyżej wymienionych sektorów, dla których dedykowana jest niniejsza platforma.

Nadmienię jeszcze tylko, iż praca ta realizowana była we współpracy (i właściwie na zamówienie) Fortis Bank Polska SA.

Jeśli moja Alma Mater zrezygnuje z prawa do pierwodruku, opublikuję ją z pewnością (najwcześniej 16.03.2010r) na łamach tej strony. Pracę można pobrać stąd. Myślę, że może ona przydać się komuś kto został postawiony przed zadaniem poznania nowoczesnych webowych technologii dla IBM i. Oprócz bowiem wspomnianego wcześniej przeglądu technologii, stworzyłem 6 aplikacji w tych technologiach i pokazałem jak wdrożyć je w zwirtualizowanym środowisku IBM i.

Lista aplikacji:

  1. CL1.CLLE – prezentacja możliwości języka CL jako podstawowego narzędzia programistycznego umożliwiającego manipulację na obiektach i5/OS oraz danych zgromadzonych w zintegrowanej z i5/OS relacyjnej bazie danych DB2.
    • funkcjonalność – wykonanie zadanego w parametrach uruchomieniowych skryptu SQL (SQLSRC47) z instrukcjami DDL i DML
    • wykorzystane technologie – CL, SQL
  2. RPG1.SQLRPGLE – pokazanie składni języka RPG, której wykorzystanie zalecane jest obecnie przez IBM.
    • funkcjonalność – insert do bazy danych w fixed-format, odczytanie zawartości tabeli CUSTOMER w free-format
    • wykorzystane technologie – zagnieżdżenie poleceń SQL w obu wyżej wymienionych rodzajach składni RPG
  3. RiaExample
    • a) prezentacja technologii związanych z JEE, które stanowić mogą alternatywę dla RPG jeśli chodzi o implementację aplikacji zawierającej logikę biznesową opartą na danych zgromadzonych w bazie danych DB2 for i5/OS.
    • b) pokazanie technologii BIRT jako alternatywy dla RPG jeśli chodzi o generowanie biznesowych raportów dla potrzeb przedsiębiorstwa.
      • funkcjonalność – aplikacja posiada dwa moduły:
        • CRUD – pozwalający na podgląd i modyfikację danych z DB2 for i5/OS
        • raporty – pozwalający na generowanie raportów do HTML, PDF, MS Word i MS Excel
      • wykorzystane technologie – aplikacja stworzona została w modelu RIA z wykorzystaniem następujących technologii: JSP, serwety, JS, zestaw komponentów JSF RichFaces, JDBC w implementacji JTOpen, Apache log4j, Eclipse BIRT
  4. WsRiaExample – stworzenie tej aplikacji, miało na celu zaprezentowanie technologii Web Services, mogącej posłużyć do zbudowania architektury softwarowej zgodnej z metodologią SOA, opartej o IBM i.
    • funkcjonalność – wystawianie usług związanych z pobieraniem i obróbką danych z DB2 for i5/OS
    • wykorzystane technologie – Apache Axis2 z modułami zapewniającymi bezpieczeństwo wysyłanych komunikatów: WS-Security (Rampart) i WS-Addressing
  5. RichClientExample
    • a) przykład podejścia do tworzenia aplikacji biznesowych przed upowszechnieniem się J2EE
    • b) przykład kienta konsumującego usługi Web Services.
      • funkcjonalność – aplikacja posiada dwa moduły:
        • program typu Rich Client, realizujący funkcjonalność CRUD
        • klient odpytujący o dane Web Service z aplikacji numer 4.
    • wykorzystane technologie – J2SE, Swing, Apache Axis2, Apache log4j
  6. GrailsRiaExample
    • a) pokazanie, że na IBM i da się uruchamiać aplikacje zaimplementowane w nowoczesnych, dynamicznych, skryptowych językach programowania, wspomagających biznes dzięki którym możliwe jest ekspresowe tworzenie lekkich serwisów internetowych, przydatnych np. w marketingu nowych produktów (w tym finasowych)
    • b) przykład obudowania technologii znakowych aplikacją z webowym interfejsem użytkownika
      • funkcjonalność
        • webowa aplikacja realizująca funkcjonalność CRUD
        • o refacing komend języka CL
      • wykorzystane technologie – Groovy, Grails, pluginy Grails: grails400utils, jtopen

Praca ta (a zwłaszcza rozdział 1.) może być także moim zdaniem przydatna dla osób, które dopiero zaczynają przygodę z platformą IBM i i poszukują rzetelnych informacji na jej temat.