Specific environment setting with Spring

There is one issue that people usually have with J2EE deployment. I call it specific environment setting. Imagine that you have several environments that you want to deploy your J2EE application to. For example development, test, stress test, staging or production. For all those environments you need specific settings like JDBC URL, JDBC user name and password, path to a directory for data export etc. The question is where to put such setting. If you store it in the WAR (JAR, EAR, SAR or other AR) you have to create specific archive for every environment.
It is not a big problem, provided you have an automated build. Nevertheless I do not like this solution. I like to have the same archive for all environments. This way I am sure that the application on production is exactly the same as the one I have tested on the test environment. Moreover, there is a problem with passwords. You have to make them part of the build process and effectively publish them by doing so.
One possible solution is to use JNDI. It is probably the best solution if you can use it. But what if you don’t use big and heavy J2EE server. What if you use a servlet container like Tomcat or Jetty instead. In this case I have one of my favorite tricks for you. I use Spring configuration similar to the following one:

 <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="locations">
		<list>
			<value>
			  classpath:main-config.properties
			</value>
			<value>
			  file:///${user.home}/spring-test.properties
			</value>
		</list>
	</property>
	<property name="ignoreResourceNotFound" value="true"/>
	<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
 </bean>

<bean class="net.krecan.spring.TstBean">
	<property name="property1" value="${property1}"/>
	<property name="property2" value="${property2}"/>
	<property name="property3" value="${property3}"/>
</bean>

Spring tries to load a configuration file from user home directory. If the file is found, properties from the main-config file are overwritten by those found in the spring-test file. And you can even overwrite those by defining a system property with the same name.
I like it. This way every developer can have his own configuration. There is no danger of accidentally committing passwords to a source control system. You can use exactly the same WAR on every environment. It is relatively safe, although some system administrators do not like it. This way you can distribute an application over the internet without forcing users to unpack it, edit a config file and pack it again afterwards.

Zatraceně líná inicializace

Dneska jsem strávil pěkných pár hodin hledáním záhadné chyby. Nedávno jsme v práci implementovali webové služby nad stávající aplikací. Protože jsem se kdysi infikoval testováním, tak jsem si v SoapUI vyrobil takovou pěknou sadu automatických testů. Nejsou to unit testy. Unit testy máme pokrytou vrstvu zařizující webové služby. Pomocí SoapUI děláme funkční testy, které otestují všechno odshora dolů. Testují webové služby, stávající kód aplikace, který není testy pokryt skoro vůbec, a dokonce i interakci s několika systémy na pozadí. Tyto funkční testy obvykle procházejí, pokud tedy počasí přeje, v databázi jsou ta správná data a lety do Amsterdamu nejsou zrovna moc v módě.
Včera odpoledne jsem si ale všiml jedné zajímavé věci. Když nastartuji server a pustím všechny testy najednou, většina z nich selže. Když jsem se podíval do logu, tak jsem uviděl pěknou řadu NullPointerException vyhozených z knihovny Castor. Tu používáme pro serializaci do XML při komunikaci s jedním systémem pod námi. Zajímavé bylo, že když jsem ty testy spustil kdykoliv později, všechny prošly (až na výjimky uvedené dříve). Chvíli jsem si pohrával s myšlenku chybu ignorovat. S Castorem nikdy problémy nebyly, používají ho spokojeně několik let. Třeba je to jen nějaká taková podivnost s kterou se přeci nemusím zabývat. No ale nakonec moje poctivé já zvítězilo a vrhl jsem se do zkoumání. Přeci jenom ve mně trochu hlodaly pochyby, že by to mohlo být nějak způsobené mojí chybou. Ano, dokonce i já o své genialitě někdy pochybuji.
Nejdřív jsem si ověřil, že se to děje pouze hned po nastartování aplikace. A to jenom když spustím několik dotazů najednou. Klasické indicie na problém se synchronizací vláken. Zkusil jsem osvědčené řešení. Zeptal jsem se pana Googla. Světe div se, ani on mi neuměl poradit. To by naznačovalo, že je chyba na naší straně. Castor býval docela populární, kdyby v něm byla chyba, někdo by na ni přeci narazil.
Ale naše použití Castoru bylo naprosto v pořádku. Je tak jednoduché, že na něm není co zkazit

Marshaller.marshal(objectToBeMarshalled, writer);

Takže jsem se ocitl zase na začátku. Dobré bylo, že k chybě docházelo vždy na tom samém řádku. Takže se dal použít debugger. Po pár hodinách strávením restartováním serveru, debuggováním a přemýšlením co dělám špatně jsem přišel na to, že se vše točí kolem třídy DurationDescriptor. Když jsem uviděl její konstruktor, bylo mi jasné že jsem narazil na viníka. Posuďte sami

    private static XMLFieldDescriptorImpl _contentDescriptor = null;


    //----------------/
    //- Constructors -/
    //----------------/

    public DurationDescriptor() {
        super(_xmlName, Duration.class);
        if (_contentDescriptor == null) {
            _contentDescriptor = new XMLFieldDescriptorImpl(String.class,
                "content", "content", NodeType.Text);
            //-- setHandler
            _contentDescriptor.setHandler(new DurationFieldHandler());
        }

    } 
    ...
    _contentDescriptor.getHandler().getValue();

Zkušení harcovníci už určitě vidí kde je problém a mlátí hlavou o stůl. Ostatním se to pokusím vysvětlit.
Představte si že mám dvě vlákna, pojmenujme si je Franta a Pepík. Oba bohužel trpí takovou ošklivou nemocí, kvůli které usínají v ten nejnevhodnější okamžik. Začněme u Pepíka. Ten skočí do konstruktoru a jukne se na statický _contentDescriptor. Ten je prázný takže se Pepík rozhodne ho naplnit. Jenže než to stihne, tak usne někde na cestě mezi řádkami 9 a 10. To je příležitost pro Frantu. Ten se vrhne do konstruktoru, mrkne na _contentDescriptor, ten je pořád ještě prázdný a tak se rozhodne ho naplnit. Obcházení spícího Pepíka ho ale unaví natolik, že z toho taky usne. Jak sebou žuchne probudí Pepíka, který je plný elánu. Vyrobí XMLFieldDescriptorImpl, nastaví Handler a vyrazí do světa. Bohužel usne těsně před tím než stihne Handler použít. Mezitím se vzbudí Franta. Ten si tolik neodpočinul, jenom si vzpomene, že chtěl vyrobit XMLFieldDescriptorImpl. Tak to udělá ale ukládání reference do proměnné _contentDescriptor ho unaví ho to natolik, že zas hned usne. Mezitím se vzbudí energičtější Pepík, který zrovna potřebuje použít Handler. Tak zavolá _contentDescriptor.getHandler(). A co dostane milé děti? null přeci. Zazvonil zvonec a já mám plný log NullPointerException.
Na obhajobu autorů Castoru musím říci, že chybu opravili v roce 2006 commitem číslo 6421. Bohužel ji opravili potichu a nikomu o ní neřekli, takže mi pan Google neuměl poradit. Opravená verze vypadá následovně.

    private static final XMLFieldDescriptorImpl _contentDescriptor;

    static {
        _contentDescriptor = new XMLFieldDescriptorImpl(String.class, "content",
                                                        "content", NodeType.Text);
      _contentDescriptor.setHandler(new DurationDescriptor().new DurationFieldHandler());
    }

Je to mnohem jednodušší a bezpečnější. JVM mi zaručí, že se statický inicializátor zavolá nejvýše jednou.
Jaké z toho plyne poučení? Jedno je evidentní. Používejte nejnovější verze knihoven. (Pokud tedy můžete, my máme Castor upravený od subdodavatele, nikdo neví jaký je rozdíl oproti standardní verzi takže se nikdo neodváží upgradovat na novější verzi).
Druhé poučení je složitější. Dejte si pozor na línou inicializaci. Není to tak jednoduché jak to vypadá. Pokud má někdo dojem, že by to vyřešilo vhodně umístěné klíčové slovo synchronize, přečtěte si tento článek. Není to vůbec jednoduché.
Používání líné inicializace je jistý druh optimalizace. Většinou se k ní uchylujeme, když máme dojem, že můžeme ušetřit pár drahocenných instrukcí. Většinou to ale nestojí za to. Pokud si opravdu myslíte, že ji potřebujete, udělejte to jak radí šílený Bob Lee. No a já se s vámi rozloučím citací zlatých pravidel optimalizace:

Pravidlo 1: Neoptimalizujte
Pravidlo 2: (jen pro experty) Zatím neoptimalizujte

Tomu říkám prosakující abstrakce

Každý problém v programování můžeme vyřešit přidáním další vrstvy abstrakce

Každá netriviální abstrakce do určité míry prosakuje.
Zákon prosakujících abstrakcí (Joel Spolsky)

Prosakující (děravá, leaky) abstrakce je termín, který zpopularizoval Joel Spolsky ve svém článku „Zákon prosakujících abstrakcí“. O co jde? V programování a obecně v informatice si potrpíme na abstrakce. Uvedu příklad ze světa Javy. Používáme ORM nástroje jako je Hibernate. Ty nám krásně odabstrahují všechny ty složitosti za tím. Nemusíme vědět nic o JDBC, SQL, Oraclu nebo Solarisu, stačí nám znát JPA a o zbytek je ukryt za stěnou abstrakcí. Prostě si jenom žijeme v krásném objektovém světe a od hrůz vrstev které jsou pod námi jsme odstíněni. To je ovšem pravda jen dokud nedojde k problémům. Jakmile něco přestane fungovat, začnou k nám prosakovat vrstvy od kterých jsme byli původně odstíněni. Takže musíme vzít kladivo a prosekat se na místo kde problémy vznikly. Takže se najednou musíme začít zabývat nastavením JDBC nebo efektivitou generovaných SQL příkazů. Když je problém hodně exotický, tak si musíme nastudovat jak fungují transakce v Oraclu, indexy nebo dokonce souborový systém v Solarisu. Ocituji Joela:

Všechno tohle paradoxně znamená, že ačkoliv máme programovací nástroje stále vyšší a vyšší úrovně s vyšší a vyšší úrovní abstrakce, je stále těžší a těžší stát se dobrým programátorem.

Kdysi programátorovi stačilo umět JDBC a SQL. Pak přišli ORM nástroje a od JDBC ho odstínili. No jo jenže chudák programátor potřebuje řešit problémy. Takže mu nestačí znát jen ORM, musí se orientovat i ve vrstvách pod ním. Dnešní programátor tak toho musí umět víc než kdysi.

No a proč to vlastně píšu. Narazil jsem na úplně úžasný příklad prosakujících abstrakcí. Mám notebook s Ubuntu. Když jdu ve Firefoxu na stránky grails.org, celý systém na pár sekund úplně zatuhne. Když se podívám co se v děje, tak vidím, že X Windows žerou 100% procesoru. Ostatní stránky ale fungují normálně. Evidentně je tedy chyba na stránce grails.org. No ale v ostatních prohlížečích stránka funguje skvěle. Takže by to bylo Firefoxem? Ale na Windows to ve Firefoxu funguje dobře. Tak že by za to mohlo Ubuntu? Jiné prohlížeče nemají s danou stránkou na Ubuntu problém. Tak čím to může být? Záhada. Naštěstí máme Google. Ten nám prozradí, že se to děje v pouze v kombinaci nVidia grafická karta, nVidia proprietární ovladač, Linux a grails.org nebo pár dalších stránek. Vypadá to, že grails.org mají v kaskádovém stylu (CSS) zadanou nějakou nesmyslně velkou hodnotu, kterou daný ovladač špatně vyhodnotí a začne počítat na CPU místo na grafické kartě. Nebo tak něco.

V tomto případě nám prosákly detaily práce grafické karty až do CSS. Zkrátka průsak skrz celý notebook. Teď si představte, že by se vám stalo něco podobného při vývoji interní aplikace. Google by vám v tom případě nepomohl. Měli byste jen chybové hlášení od pár uživatelů a vůbec byste mu nevěřili. Vždyť to všem ostatním funguje. Vždyť je to webová stránka, ta je přeci nezávislá i na operačním systému, natož pak na hardware. A ono ne. Ovladač grafické karty, operační systém i prohlížeč jsou jen vrstvy abstrakce. A jelikož jsou netriviální tak občas prosakují.