Hibernate Search – fulltext nad Hibernate ORM

Pokud chceme do naší Java aplikace integrovat fulltextové vyhledávání, dříve či později skončíme u Apache Lucene, frameworku, který je pro vyhledávání v Javě v podstatě standardem a synonymem. Pokud se rozhodneme používat čistý Lucene, musíme mj. řešit:

  • mapování doménových objektů do Lucene (v Lucene jsou všechny atributy obyčejné řetězce)
  • mapování výsledků vyhledávání zpátky do doménových objektů
  • transakce
  • otevírání a zavírání indexu
  • zamykání indexu, v 1 okamžiku může do indexu zapisovat pouze 1 proces
  • škálování fulltextu přes více serverů

Jestliže se však držíme zavedených řešení a používáme Hibernate (ať už Hibernate Core nebo Hibernate jako JPA providera), můžeme využít rozšíření Hibernate Search.
Hibernate Search je projekt, který Hibernate Core doplňuje o fulltextové prohledávání persistovaných objektů a funguje jako fasáda nad Lucene.

Hibernate Search nabízí:

  • API nad fulltextem podobné JPA
  • odstínění od low-level objektů Lucene-nestaráme se vůbec o otevření/uzavření indexu, o zamykání indexu 1 procesem, atd.
  • fulltextové vyhledávání nad persistovanými objekty pomocí Lucene tříd a objektů
  • automatický převod objektů do indexu a zpět (pokud nevyhovuje/nepostačuje, lze napsat převodníky)
  • automatickou synchronizaci indexu s doménovými objekty
  • reindexaci již persistovaných objektů
  • výsledky vyhledávání jsou kompletní objekty, Hibernate Search vrátí na dotaz seznam vyhovujících ID a Hibernate pak načte z db objekty s těmito ID
  • transakční chování – zápis do fulltext indexu se provádí až nakonec po commitu změn do db
  • škálování jako master/slave pomocí JMS
  • jednoduchost, pokud známe Lucene a Hibernate, můžeme jej začít používat okamžitě.

Ukázková aplikace

Pro praktickou ukázku integrujeme fulltextové vyhledávání do jednoduché webové aplikace pro správu firem (klasická create-read-update aplikace).

Použitý stack

  • Hibernate Entity Manager 3.4.0.GA - persistence
  • Hibernate Search 3.1.0.GA – fulltext
  • Apache Maven 2.0.9 – project management
  • Apache Wicket 1.3.5 – prezentační vrstva
  • Spring 2.5.5 – lepidlo
  • PostgreSQL 8.2-relační databáze

Poznámka k následujícím zdrojovým kódům: uvádím pouze soubory, které se bezprostředně týkají Hibernate Search, kompletní zdrojáky aplikace jsou na konci příspěvku.

Závislosti

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.hradil</groupId>
    <artifactId>HibernateSearchSimpleApplication</artifactId>
    <packaging>war</packaging>
    <version>1.0</version>
    <name>Hibernate Search Simple Application</name>

    <dependencies>

		<!--  wicket -->
        <dependency>
            <groupId>org.apache.wicket</groupId>
            <artifactId>wicket</artifactId>
            <version>1.3.5</version>
        </dependency>

        <!-- wicket+spring -->
        <dependency>
            <groupId>org.apache.wicket</groupId>
            <artifactId>wicket-spring-annot</artifactId>
            <version>1.3.5</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>3.4.0.GA</version>
        </dependency>

        <dependency>
            <artifactId>hibernate-annotations</artifactId>
            <groupId>org.hibernate</groupId>
            <version>3.4.0.GA</version>
        </dependency>

        <!-- Hibernate Search -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-search</artifactId>
            <version>3.1.0.GA</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring</artifactId>
            <version>2.5.5</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

		<!-- LOGGING DEPENDENCIES - LOG4J -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.5.2</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.5.2</version>
        </dependency>

        <!-- work around for jetty commons logging issue -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.5.2</version>
        </dependency>

        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>

        <dependency>
            <groupId>postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>8.2-507.jdbc3</version>
            <scope>compile</scope>
        </dependency>

		<!--  JUNIT DEPENDENCY FOR TESTING -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.2</version>
            <scope>test</scope>
        </dependency>

		<!--  JETTY DEPENDENCIES FOR TESTING  -->

        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty</artifactId>
            <version>6.1.4</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**</include>
                </includes>
                <excludes>
                    <exclude>**/*.java</exclude>
                </excludes>
            </resource>
        </resources>
        <testResources>
            <testResource>
                <directory>src/test/java</directory>
                <includes>
                    <include>**</include>
                </includes>
                <excludes>
                    <exclude>**/*.java</exclude>
                </excludes>
            </testResource>
        </testResources>
        <plugins>

            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>maven-jetty-plugin</artifactId>

                <configuration>
                    <scanIntervalSeconds>5</scanIntervalSeconds>
                    <contextPath>/</contextPath>
                </configuration>

            </plugin>

            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>

        </plugins>
    </build>

</project>

Konfigurace

applicationContext-jpa.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
       >

    <description>
        Konfigurace kontextu pro JPA, vcetne datovych zdroju a transakcnich manazeru.
    </description>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <description>
            Datovy zdroj pro persistentni vrstvu. Obsahuje udaje o pripojeni k databazi.
        </description>
        <property name="driverClass" value="org.postgresql.Driver"/>
        <property name="jdbcUrl" value="jdbc:postgresql://localhost/HibernateSearchSimple"/>
        <property name="user" value="postgres"/>
        <property name="password" value=""/>
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <description>
            Tovarna pro manazer entit. Je pouzita trida LocalContainerEntityManagerFactoryBean,
            ktera je doporucena pro produkcni nasazeni JPA.
            Viz. http://static.springframework.org/spring/docs/2.5.x/reference/orm.html
        </description>

        <property name="dataSource" ref="dataSource"/>

        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
                <property name="generateDdl" value="false"/>
            </bean>
        </property>

      <!-- nastaveni JPA a Hibernate Search -->

        <property name="jpaProperties">

            <value>

            # konfigurace JPA
            hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
            hibernate.hbm2ddl.auto=validate

            # debugging / logging
            hibernate.show_sql=true
            hibernate.format_sql=true
            hibernate.use_sql_comments=true

            # konfigurace Hibernate Search
            # kde bude ulozen Lucene index
            hibernate.search.default.indexBase=/tmp/index

            </value>

        </property>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!-- transakcni manazer -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

</beans>

Entita

Company.java

package org.hradil.search.entity;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import org.hibernate.search.annotations.DocumentId;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;

/**
 * Entita reprezentuje firmu.
 * Firmu lze ve fulltextu vyhledat podle id, name a regNo.
 *
 * @author jirka@hradil.org
 */
@Entity
@Table(name = "company")
@Indexed //tridu budeme chtit indexovat ve fulltextu
public class Company implements Serializable {

    private static final long serialVersionUID = 1216348069826762176L;

    @Id
    @Column(name = "id")
    @SequenceGenerator(name = "company_id_seq", sequenceName = "company_id_seq")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "company_id_seq")
    @DocumentId //primarni klic objektu ve fulltextu, zaroven podle nej muzeme vyhledavat
    private int id;

    @Column
    @Field //ve fulltextu chceme hledat firmu podle nazvu
    private String name;

    @Column
    @Field //ve fulltextu chceme hledat firmu podle IC
    private String regNo;

    /**
     * Vytvori novou firmu.
     */
    public Company() {
    }

    /**
     * Vytvori novou firmu a predvyplni atributy.
     * @param name nazev firmy
     * @param regNo IC
     */
    public Company(final String name, final String regNo) {
        this.name = name;
        this.regNo = regNo;
    }

    /**
     * Vrati id firmy
     * @return id
     */
    public int getId() {
        return id;
    }

    /**
     * Nastavi id firmy
     * @param id id
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * Vrati nazev firmy.
     * @return nazev
     */
    public String getName() {
        return name;
    }

    /**
     * Nastavi nazev firmy.
     * @param name nazev
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Vrati IC firmy.
     * @return IC
     */
    public String getRegNo() {
        return regNo;
    }

    /**
     * Nastavi IC firmy.
     * @param regNo IC
     */
    public void setRegNo(String regNo) {
        this.regNo = regNo;
    }
}

Servisní vrstva - uložení, úprava, vyhledání

CompanyServiceImpl.java

package org.hradil.search.service;

import org.hradil.search.entity.Company;

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.jpa.Search;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * Implementace sluzby pro firmu.
 *
 * @author jirka@hradil.org
 */
@Transactional(propagation = Propagation.REQUIRED)
@Service
public class CompanyServiceImpl implements CompanyService {

    @PersistenceContext
    private EntityManager em;

    /**
     * {@inheritDoc}
     */
    @Override
    public void addCompany(Company newCompany) {
        em.persist(newCompany);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true)
    public List<Company> findCompanyBy(final String fulltextQuery) {

        Assert.notNull(fulltextQuery, "retezec pro vyhledavani nesmi byt null!");

        //vezmeme instanci manazeru fulltextu
        FullTextEntityManager ftEm = Search.getFullTextEntityManager(em);

        //vytvorime parser pro prohledavane atributy firmy, pouzijeme standardni analyzer
        QueryParser parser = new MultiFieldQueryParser(new String[]{"id", "name", "regNo"}, new StandardAnalyzer());

        //vytvorime dotaz do Lucene
        org.apache.lucene.search.Query luceneQuery;

        //pokud po zruseni vsech bilych znaku a hvezdicek zustane jen prazdny retezec, pak vracime vsechny zaznamy
        //napr. dotaz "**** * **  *" bude vyhodnocen tak, ze chceme prohledavat vsechny firmy
        if (StringUtils.trimAllWhitespace(StringUtils.deleteAny(fulltextQuery, "*")).isEmpty()) {
            luceneQuery = new MatchAllDocsQuery();
        } else { //byl zadan retezec, vyhledavame
            try {
                luceneQuery = parser.parse(fulltextQuery); //zparsujeme predany dotaz pomoci parseru Lucene
            } catch (ParseException e) { //neplatny dotaz, prekonverujeme na runtime vyjimku, nemusime zachytavat
                throw new RuntimeException("Neplatny dotaz do fulltextu: " + fulltextQuery, e);
            }
        }

        //vytvorime normalni JPA dotaz, ale pres rozhrani fulltextu
        FullTextQuery query = ftEm.createFullTextQuery(
                luceneQuery,
                Company.class);

        //tridime dle relevance DESC, pote dle id DESC
        SortField[] sortFields = new SortField[2];
        sortFields[0] = SortField.FIELD_SCORE; //relevance, default DESC
        sortFields[1] = new SortField("id", SortField.INT, true); //id, je to intener, DESC=true
        Sort sort = new Sort(sortFields);

        //pridame trideni do dotazu
        query.setSort(sort);

        //a vratime rovnou vyhovujici seznam firem z ORM
        return query.getResultList();

    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true)
    public Company findCompanyBy(int id) {
        return em.find(Company.class, id);
    }

    @Override
    @Transactional(readOnly = false)
    public void updateCompany(Company company) {
        em.merge(company);
    }
}

Zdrojové kódy aplikace

hibernatesearchsimple

Konfiguraci a rozjetí v clusteru si ukážeme v některém dalším příspěvku.

Levná hardwarová infrastruktura

Pro jednoho z našich klientů navrhujeme HW infrastrukturu a vzhledem k přemrštěným cenám za značkové servery známých výrobců (IBM, Sun, HP…) padnul návrh na vybudování clusteru pro aplikační kontejnery z “neznačkových” desktopů, které bychom použili jako servery.  Myšlenka má původ v Google (zdroj: David A. Vise, Mark Malseed: Google Story, Pragma 2007, ISBN: 978-80-7349-034-8), kde je nestabilita desktopů vyvážena jejich množstvím. Pokud některý z  desktopů-nodů vypadne,  zafunguje fail-over a load balancer na něj přestane posílat požadavky. Klient v nejhorším případě zaznamená výpadek v řádu vteřin.

Klady řešení:

  • cena, pokud bychom desktop=nod poskládali ze značkových komponent, pak je cena jednoho tohoto nodu v řádech tisíců
  • jednoduchý upgrade - nod by mohl být při výpadku či upgrade nahrazen rychlejším, poskládaným z aktuálních “best of breed” komponent
  • dostupnost - nod lze poskládat z komponent, nakoupených v jakémkoli supermarketu společně s rohlíky
  • nezávislost na dodavateli - sbohem obchodníkům, prodávajícím řešení, kterým nerozumí nebo na kterých se chtějí napakovat ;)

Zápory řešení:

  • možná nekompatibilita komponent - paměť od výrobce ABC  si nerozumí s deskou výrobce XYZ, což nemusíme poznat okamžitě, nod může vykazovat nestabilitu náhodně, lze řešit intenzivním testováním
  • poskytování SLA - infrastrukturu musíme podporovat sami, ideálně mít nakoupeno několik desktopů do zásoby a při výpadku nod rovnou vyměnit za jiný
  • požadavky na prostor - pokud by nod byl umístěn v běžném mini či miditoweru, zabírá více prostoru, než hezké 1U či 2U skříně “značkových” výrobců. Nicméně desktopy lze do skříní montovat taky.

Co je třeba dále zvážit:

  • pozor na SPOF (single point of failure), pokud např. máme relační databázi, která fail-over na jiný server neumí, potencionálně nestabilní desktop tady nelze použít
  • disky použité v nodu, protože na nod bude deploynutý jen .war aplikace a relační databáze je umístěna v jiné vrstvě, stačí nám levné disky, zapojené do prostého RAID 1 (mirror), abychom při výpadku jednoho disku neohrozili stabilitu aplikace

Použité technologie:

Použil jste někdo podobnou infrastrukturu v ostrém provozu?

Definitivně: Oracle kupuje Sun

Aktuální informace přímo od zdroje - Oracle a Sun Microsystems vstupují do konečné fáze dohody, podle které Oracle koupí Sun za cca 7.4 miliardy dolarů. Oracle tak získává Javu, Solaris a ostatní technologie (zdroj: Sun Microsystems: Oracle to buy Sun). Vedle nezajímavých řečí o 20-letém partnersví a obrovském přínosu pro komunitu, uživatele, atd. bude velmi zajímavé sledovat skutečný důsledek této akvizice.

Sun byl sice skvělý technologický inovátor, ale svým technologiím nedokázal přinést vhodný obchodní model. Takže se necháme překvapit, co Oracle konkrétně s Javou provede. Co myslíte?

Postřehy z agilní praxe

Proces vývoje software odráží několik posledních let jasný trend - nestálost prostředí a připravenost na změny. Pokud shrneme několik agilních metodik (extrémní programování, Scrum, RUP), tak řeší v podstatě naprosto stejné věci-krátké iterace, testy, otevřenost, zeptej se kódu, atd.  Některé z těchto pravidel rozeberu.

Vyvíjíme po malých částech, čili v iteracích=cyklech=sprintech. Jedním z důvodů je nestálost prostředí, ve kterém má být software používán (např. situace na trhu či legislativa), ale hlavně neschopnost a nemožnost vyprodukovat stabilní analýzu systému, podle které se dá programovat. Analytiky software považuji za dinosaury, jejichž doba dávno skončila a při vývoji software jednoduše nejsou potřeba. Základ software z pohledu uživatele má navrhnout klient a proveditelnost návrhu určují koneční vývojáři. Setkal jsem se s tím, že analytik (který nikdy neprogramoval), vygeneroval stovky stránek krásných diagramů a dokumentů, které si nechal nebohým klientem schválit. Klient samozřejmě netušil, co ty obrázky znamenají (když ony byly tak hezky barevné…). Poté se tento stoh dokumentů předhodil vývojářům, kteří obrázkům taky nerozumněli, ale dostali jasný pokyn - “programujte!” Po několikaměsíčním vývoji při prezentaci systému klient zjistil, že vlastně teď potřebuje už něco úplně jiného a software se do produkce vůbec nedostal.

Doporučení:

  • iterace je dlouhá max. 1 měsíc, v počátcích vývoje kratší, klidně i 1 týden
  • vývojáře musíme protlačit ke klientovi, protože jedině zadavatel a tvůrce systému umí pokládat správné otázky a generovat správné odpovědi
  • po každé iteraci následuje prezentace klientovi, který potvrdí aktuální verzi a na jejím základě generuje požadavky další
  • popis systému je obsažen ve zdrojovém kódu a v uživatelských zadáních
  • podrobná dokumentace je zbytečná - vývojáři ji nepotřebují, tvůrce ji musí aktualizovat a koneční uživatelé jsou tak frustrovaní množstvím software, který musí ovládat, že ji číst nebudou. Složitá sice může být problémová doména, kterou software řeší (třeba statistický software), ale nikdy nesmí být složitý software samotný. Pokud např. posadíme statistika k našemu statistickému softwaru, musí ho být schopen do několika minut bez problémů ovládat, protože ovládá problémovou doménu, do které je náš software zasazen a kopíruje ji.

Testujeme - test simuluje používání systému uživatelem. Ať už to schováno pod pojmy test jednotkový (na úrovni metody), integrační (na úrovni větší části systému), zátěžový, opičí, či jiný, smyslem testu je software nastartovat, aby ukázal, co umí. Testy se mají psát průběžně a to z toho důvodu, že na konci je už nikdo psát nebude. Stejně tak s každým testem vytvoříme nekonečného automatického robota, který nám bude hlásit, jestli pořád software dělá, co dělat má. Psaní testů z krátkodobého pohledu zdržuje, ale dlouhodobě nám právě testy drží software stabilní.

Doporučení:

  • kontrolovat pokrytí software testy (např. v Javě přes Cobertura), stanovit si hranici, pod kterou nesmíme jít, ideálně nad 80%)
  • maximálně urychlit psaní a spouštění testů (mock objekty, databáze v paměti)
  • nepřetržitě integrujeme pomocí integračního serveru (Continuum, Hudson, Bamboo…)

Jsme malí - neudržujme velké týmy. Čím více vývojářů, tím více tření, komunikace, rozhodování, administrativy a neefektivity, kterou nakonec (zbytečně) platí klient.

Doporučení:

  • ideální velikost týmu je 3-5 vývojářů, pokud se jich vývoje účastní více, uděláme více týmů a každý z nich má svou problémovou doménu. Tyto týmy však musí být vzájemně synchronizovány, např. pomocí scrum of scrums-porad, kterých se účastní pouze vyhrazený člověk z každého týmu, nikoli všichni členové  dohromady.

Posaďme tým dohromady, čili  “seat the team together”. Tým musí sedět pohromadě. Tečka. Tohle je nejrychlejší způsob řešení komplikací během vývoje a udržení synchronizovaných znalostí o systému. Pokud má kdokoli v týmu otázku, může ji položit a dostane se mu okamžité odpovědi.

Doporučení:

  • respektovat klid pro práci - pokud máme otázku, nekřičíme na celou místnost, ale zeptáme se, zda můžeme vyrušit, v extrémních případech si zavedeme každou hodinu několik minut na otázky a odpovědi
  • červená čepice - jednoduché pravidlo - v týmu je k dispozici symbol “červené čepice” (ať už cedulka, nebo opravdová kšiltovka), kdo ji má na hlavě, ten nesmí být rušen
  • nepracovat z domova přes Internet, bohužel, zatím se ukazuje, že je stále efektivnější být fyzicky pohromadě v 1 místnosti, než komunikovat přes Skype, Jabber, atd. Budu rád, když mi tuto neefektivitu někdo vyvrátí a přidá praktické zkušenosti.

IBM kupuje Sun? Co bude s Javou?

Že je Sun na prodej, je všeobecně známá informace. Stejně tak i varianta, že Sun bude koupen IBM.

Dopady na některé technologie Sunu v případě, že kupcem bude IBM, mohou být následující:

Servery a zálohovací řešení (HW) - IBM měl poslední rok podíl na trhu serverů 31.4%, největší rival HP 29.5%, zatímco Sun 10.6% (zdroj: The Wall Street Journal: IBM in Talks to Buy Sun in Bid to Add to Web Heft ). IBM by si připsal podíl Sunu na trhu a získal by jednoznačný náskok. Servery by se prodávaly pod značkou IBM, dělit produktovou řadu je zbytečnou investicí do marketingu. Sbohem SunFire a SunStorage, zákazníkům je to jedno, uvnitř je to stejně všechno na jedno brdo.

Aplikační servery - Sun má Glassfish Enterprise Server (dříve Sun Application Server),  postavený na open-source Glassfish Application Server, IBM má WebSphere Application Server (WAS). Tříštit síly jak vývojářů, tak technologie nemá z pohledu IBM smysl. Předpokládám, že IBM se pokusí zákazníky přemigrovat na WAS  a open-source Glassfish ponechá svému osudu. Pokud nebude existovat komerční podpora pro Glassfish, tento server z dlouhodobého pohledu zanikne, protože svěřit jej (pouze) do rukou komunity znamená pomalu jej odsoudit k smrti. Velcí, ani normální zákazníci si Glassfish bez podpory nenaimplementují. Vývojáři, používající Glassfish pro Javu, přemigrují na jiný aplikační server. Pokud se drží standardů, změn by nemělo být moc (až na nějaké ty špeky, viď Dagi :) ).

Vývojové nástroje - Sun má NetBeans, IBM má mj.  IBM Websphere Studio Application Developer, který jede nad Eclipse. Z mého pohledu nemá NetBeans nic revolučního, co by Eclipse chybělo a domnívám se, že vývoj NetBeans bude pozastaven. Pro vývojáře, kteří používají NetBeans to bude znamenat několik dnů zkoušení jiného IDE, ale dopad na jejich vývoj bude minimální.

Databáze - Sun koupil MySQL a vlastní tak nejpopulárnější open-source databázi na světě, IBM má DB2. Jakkoli může být DB2 vnímána jako “enterprise” databáze, dnešním trendem je za základní software (databáze nevyjímaje) neplatit. MySQL nezanikne, není to soupeř DB2, IBM bude MySQL brát spíše jako “vstupní” databázi pro menší projekty, či firmy, které chtějí ušetřit (a kdo nechce).  A samozřejmě nabídne k MySQL, jak jinak, placenou podporu.

Java (platforma) - nejdůležitější nakonec. IBM má svou Javu, IBM Developer Kit, který prochází TCK a je tak ověřeno, že splňuje specifikace JSR, tedy měla by to být plnohodnotná, kompatibilní Java. Javu od IBM jsem nikdy nezkoušel, držel jsem se vždy Javy od Sunu, takže praktické zkušenosti rád přenechám zasvěcenějším. V budoucnu tak bude jen jedna Java a pár další, naprosto nevýrazných a nepoužívaných komunitních implementací. Udržovat a rozšiřovat jazyk a platformu, jako je Java, která je snad jediným konkurentem Microsoftích technologií, si může dovolit jen velká společnost a IBM by se toho mohla ujmout dobře.

O Javu jako platformu strach nemám. Tato technologie je natolik používaná a zaběhnutá, že její pád je v krátkodobém a střednědobém období nepravděpodobný. Alternativ k Javě moc není, napadá mě Microsoftí .NET Framework, případně multiplatformní Mono. Hromadný přepis již existujícího Java software je nemožný, existující projekty musí minimálně dožít a ty nové se stejně staví v aktuálních technologiích. A tam už je jedno, jestli je to Java verze 1.6,  1.7 nebo třeba .NET, či jiný, lesklejší a radostnější jazyk.

O marketingu a šeptandě

Bez marketingu nemůže existovat žádný produkt, softwarový nevyjímaje. Jednou z velmi efektivních a dnes používaných forem marketingu je word of mouth, čili “šeptanda”.

Placená reklama je drahá a krátkodobá.  Lepší je mít produkt tak vyčnívající z řady, že si na něj reference předají uživatelé sami. Kromě vlastní zkušenosti neexistuje důvěryhodnější zdroj, než spokojený kamarád, který software doporučil. Třeba Google, v době uvedení služby Gmail, šokoval velikostí schránky 1 GB a také systémem pozvánek, kterých bylo omezené množství. Google si tak otestoval postupné škálování celého systému a zároveň vzbudil u uživatelů, vlastnících účet, dojem exkluzivity. O Gmailu se hodně mluvilo, sám jsem o něm v počátcích psal. Samotné datum uvedení - 1.4.2004, tedy na apríla, je dalším výborným tahem, který zajistil, že byl Gmail chápán jako vtip (tak velká schránka přece neexistuje) a o to víc se o něm mluvilo a psalo. Stejně tak označení Beta (po 5 letech v produkci) je  nejen omluvou za případné potíže, ale i potvrzením, že používáte čerstvý software, který ještě nevychladnul a jste “in”.

Gmail však přežil hlavně díky tomu, že byl a stále je jednoduchý. Dobře se používá a prostě funguje. Rozbořil zažité koncepty (nepotřebujete složky, atd.). Je jiný a proto uspěl.

A závěr tohoto příspěvku pro vývojáře software? Odlišujte se. Přemýšlejte, čím váš software vybočuje z řady. Uniformních řešení jsou dnes stovky. Vy píšete software, který si zaslouží být jiný.  Mějte však na zřeteli, že software píšete pro lidi, ne pro technologii samotnou. Protože jedině v případě, že se váš software používá a koncoví uživatelé jsou s ním spokojeni, jste jako vývojáři uspěli.

Přednáška Apache Wicket na CZJUG

V rámci březnového setkání české Java User Group (CZJUG) na téma “webové frameworky“  jsem připravil a odprezentoval přednášku o webovém frameworku Apache Wicket.

Abstrakt:

Cílem prezentace je uvedení frameworku Apache Wicket pro tvorbu web aplikací, založených na platformě Java. Apache Wicket nabízí srozumitelný komponentový model, umožňující tvořit aplikaci pomocí běžných, http protokolem nezatížených javovských tříd, podporuje jednoduchou tvorbu prezentační části pomocí standardního HTML a přebírá odpovědnost za řízení konverzace mezi klientem a serverem. Wicket se snaží vycházet ze známých, ověřených principů a nenutí vývojáře učit se novou syntaxi, kterou jinde nelze použít. Prezentace je ve znamení jednoduchosti, která se prolíná celým frameworkem a ukazuje, že pro tvorbu komplexních web aplikací je Apache Wicket správnou volbou.

Průběh přednášky byl z mého pohledu velmi zajímavý a příjemně mě překvapily velmi dobře mířené dotazy od aktivních posluchačů. Doufám, že jsem o Wicket vzbudil zájem, který si tento framework určitě zaslouží.

Prezentace v PDF

Kompletní zdroje k prezentaci včetně zdrojových kódů

EJB - potřebujeme střílet vrabce “kanónem”?

Tímto chci všem zůčastněným poděkovat za podnětné připomínky k mým EJB příspěvkům.
Rád bych reagoval především na jAbLoK spot “EJB 3.0 aneb Proč že tu mrtvolu stále resuscitují?“od Pavla Kolešnikova, který jsem pochopil jako vyčítání komplikovanosti specifikace EJB 2.x pro jejich nutná rozhraní, záznam v DD, či vyhazovaní vyjímek. Pavel staví rovněž otázku, zda jsou EJB vhodné pro triviální webové aplikace.
Ale co když nechceme psát “jednoduchou” webovou aplikaci, ale skutečně systém, který musí být škálovatelný a robustní, kde musíme používat clustering a load balancing a kde uvítáme, že se pro vyžadovanou spolehlivost a složitost rádi soustředíme hlavně na business implementaci?

EJB přece ví a počítají s tím, že budou použity pro rozsáhlé projekty a zastřešují hlavní potřeby vývojářů tím, že se na sebe snaží převzít do maximální možné míry tolik potřebný “background”. IDE díky jasným pravidlům umožňují tvořit komponenty velmi rychle a celý návrh tak minimalizuje množství chyb. Že jsou třeba k beanu minimálně 2 další rozhraní (pominu-li message-driven beans)? No a? Pravidla jsou tak dána, umožňují nám pak určitou funkčnost a je na každém, zda mu tato vyhovuje, či ne.

Technologií pro Javu je velmi mnoho a nelze jednoznačně říct, že EJB jsou všelékem na veškeré problémy, které musíme řešit. Místo porovnávání “kdo z koho” je rozumných východiskem zvážit, čím může být konkrétní technologie přínosem pro náš projekt. Pokud se najde vhodnější kandidát pro naše potřeby, bylo by hříchem jej nepoužít. Právě volnost rozhodování nám dává prostor pro další zdokonalování a psaní kvalitnějšího software. A pro mě jsou EJB vhodnou technologií.

Studujeme EJB

Pro studium Enterprise Java Beans je nezbytně nutná vhodná literatura, která nás provede všemi zákoutími této technologie. Musí nás přesvědčit o tom, že Sun měl ty nejlepší úmysly a skutečně se nám snažil usnadnit náš nelehký vývojářský život.
Zdrojů ke studiu je mnoho a podle dotazů a zdůvodnění, proč EJB nepoužívat mám spíše dojem, že to spousta zvědavců vzdala už na začátku a vůbec se nesnaží pochopit podstatu této skvělé technologie. Vůbec se jim nedivím, pokud studovali podle J2EE tutorialu. Jsou ale mnohem jednodušší cesty.

Head First EJB (EJB 2.0) je vysněnou knihou, která nám jasně ukazuje, jak má vypadat správný a srozumitelný výklad. Musím rovnou říct, že nenávidím zbytečně složité věci a pokud mám něco dobře pochopit, musím si to nakreslit. Právě tento způsob používají autoři Kathy Sierra a Bert Bates a celá kniha je koncipována jako co nejjednodušší (a zároveň velmi kvalitní) příprava na certifikaci Sun Certified Business Component Developer (SCBCD). Témata jsou do hloubky probírána, perfektně rozkreslena a jsou podána velmi nenásilným způsobem. Čtenář pochopí koncepci EJB rozhraní, typy beanů, životní cyklus beanu a vůbec všechno, co potřebuje, aby mohl začít psát skutečné SW komponenty.
Dostupnost: v ČR jsem ji ještě neviděl, objednával jsem přímo z Amazon.
Hodnocení: jedna z nejlepších investicí, které jsem kdy udělal. Pokud potřebujete dokonale vysvětlit EJB, tak není důvod váhat. Nic lepšího se mi zatím do rukou nedostalo (ne, nemám procenta z prodeje ;).

Mastering EJB II (EJB 2.0) je další hutná kniha, kterou si lze zdarma stáhnout ze serveru TheServerSide. Méně srozumitelná, než HF EJB, ale v některých případech jde více do hloubky (patterns, load balancing, clustering). Má několik chyb a není tak úplná jako HF EJB (a book errata mail není funkční), ale nabízí prostě jiný pohled a rozhodně rozšíří vědomosti.
Dostupnost: volně ke stažení po registraci.
Závěr: Velmi dobrý zdroj, doporučuji číst až po zvládnutí základů.

J2EE Tutorial 1.4 (EJB 2.1) nabízí úvod do technologie EJB (společně se servlety, jsp a jsf), ale nějak mi tento způsob výkladu nepadnul do oka. Prostě vysvětlují jednoduché věci složitě a nemají tolik obrázků :). Chybí motivace, proč je třeba technologii používat určeným způsobem. Pak se nedivím, že vývojáři po shlédnutí EJB zavrhnou pro jejich příllišnou “složitost”.
Dostupnost: volně ke stažení
Závěr: Od Sunu, stačí jen prolétnout, není nutno studovat :)

EJB 2.0 Specification je prostě základ, na kterém staví všichni ostatní. Sem si chodím pro radu, když něco potřebuji vysvětlit dokonale a do hloubky “doladit”.
Dostupnost: volně ke stažení
Závěr: Pokud neznáte EJB, je zbytečné začínat tady, opět směruji dychtivé studenty na HF EJB. Co není v této specifikaci, není ani v certifikačním testu SCBCD.

Na konec se ještě zmíním, proč se nyní učím EJB 2.0 (a ne 2.1, či 3.0):

  • studijní materiály jsou psané většinou pro 2.0
  • Sun certifikuje opět pouze 2.0
  • pokud zvládnu základy 2.0, pak nebude problém přejít na novější technologii

Objevujeme EJB

Jsou technologie, které jsou všeobecně známé a používané a technologie všeobecně známé a nepoužívané. Po několika měsících experimentování s Hibernate jsem došel k názoru, že to ještě není to pravé ořechové a začal jsem se učit a zkoušet Enterprise Java Beans (EJB). Po nastudování několika knih mám pocit, že je tato technologie přímo zrozená k tomu, aby byla objevena a masivně používána. Nebudu tady popisovat, o čem jsou EJB (to už udělali lépe jiní), jen vypíchnu, co se mi na nich líbí a co ne:

Líbí:

  • Striktně komponentový přístup
  • Definice home/remote (local/local-home) rozhraní k beanu (fakt, mě se tato logika velmi líbí, když pochopíte, o čem to je, nedáte na to dopustit)
  • Striktní definice v názvech metod (ejbCreate, ejbHome…) a vůbec držení vývojáře maximálně zkrátka (řeči o volnosti neberu, když se na projektu střídá hafo lidí, tak někdy ani jmenné konvence nestačí)
  • Container Managed Persistence (CMP) (při deploy create table, atd.) a relationships
  • DD ejb-jar.xml
  • Miluju Sun a tohle dělá Sun :)

Nelíbí:

  • Specifikace EJB je místy příliš obecná (i když, zase vendoři serverů mají volnější ruce, např. load balancing, clustering, generace PK…)
  • EJB-QL je dost chudý (např. 2.0 nemají ani ORDER BY)
  • Složitá technologie, ale jen na začátku. Když pochopíte, jak vše funguje a hlavně proč je to tak uděláno, nedáte na EJB dopustit. Chce to dobrou literaturu a hned musím říct, že J2EE tutorial od SUNu dobrý zdroj není (v dalším příspěvku uvedu lepší materiály ke studiu).

Jak tak procházím java konferenci, či různé příspěvky, mám z nich pocit, že EJB se nepoužívají jednoduše proto, že spoustě lidí připadají příliš složité (což nejsou). Možná je ale pravda jinde-prostě je všichni používají, ale nepíšou o tom :).