Skip to content

Entries from May 2010.

Commons logging configuration under WebSphere

Sometimes you want to customize logging level for given package in your application (to see tracing details for example). If you're using commons-logging library the configuration file is called "commons-logging.properties" and it should be places somewhere on classpath.

org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
priority=1

Then we can configure SimpleLog back-end just declared in simplelog.properties:

org.apache.commons.logging.simplelog.defaultlog=warn
org.apache.commons.logging.simplelog.log.com.mycompany.package=debug

You place those two files on classpath, redeploy application and ... NOTHING HAPPENS. Those files aren't visible!

Why is it happening? Here's the explanation:

  • Default WebSphere class-loader is defined as PARENT_FIRST, that means WebSphere classpath is being searched in first place
  • WebSphere ships with commons-logging.properties that selects JDK 1.4 logging back-end

That's why your commons logging configuration is not visible.

How to fix that: place files inside /opt/IBM/WID61/pf/wps/properties directory (it's the first entry on the classpath). Then those files will become visible and log level customisation is possible then.

Błąd w systemie transakcyjnym mBanku

Kiedy człowiek dorasta to zwykle zakłada rodzinę i zaciąga kredyt na mieszkanie żeby mieć gdzie tę rodzinę pomieścić. Konieczność terminowego spłacania swoich zobowiązań powoduje, że warto kontrolować poziom wydatków względem przychodów na koncie w banku by uniknąć przykrych niespodzianek (patrz: przypadek Grecji). Narzędzia raportujące wbudowane w system transakcyjny mBanku ułatwiają taką operację. Przynajmniej ułatwiały - do czasu ostatniej aktualizacji oprogramowania.

Przelewy wychodzące zwykle związane są z kosztami stałymi które ciężko jest zoptymalizować (kredyt, czynsz, media). Kiedy trzeba kontrolować koszty warto więc spojrzeć na historię operacji na karcie. Możemy (czasami z zaskoczeniem) zobaczyć ile wydaliśmy np. w ciągu ostatniego miesiąca. Ustawiamy więc filtrowanie na 1 miesiąc:

i oglądamy wyniki:

Wyniki wyglądają sensownie, więc może teraz sprawdzić historię za ostatnie dwa miesiące?

W podsumowaniu operacji czeka nas niespodzianka:

Dane wyglądają nieco, hm, nierealistycznie. Analizując szczegółową listę operacji widać wyraźnie, że podsumowanie nie odzwierciedla sumy z listy. Obroty na rachunku są źle sumowane.

Kiedy ten błąd jest widoczny? Muszą być spełnione dwa warunki:

  • lista operacji na koncie nie mieści się na jednej stronie wyników i aktywuje się stronicowanie wyników
  • dla danego konta jest aktywny kredyt hipoteczny z mechanizmem bilansującym (tzw. mPlan)

W przypadku aktywowania się stronicowania podsumowanie gubi informację o wybranym rodzaju operacji.

Dobry proces testowania powinien uwzględnić przypadki szczególne (aktywny mPlan + stronicowana lista wyników). Z tego co widzę taką kombinację pominięto przy wprowadzaniu nowej wersji oprogramowania w mBanku. Problem zgłosiłem, zobaczymy ile czasu Bank potrzebuje na reakcję w przypadku takiego (dość prostego) błędu. Mam nadzieję, że procedury wpływające bezpośrednio na stan konta nie mają tego typu defektów ;-)

MySpace vs Facebook vs Twitter uptime comparision

Most of us are using Web2.0 sites but massive user base that logins every second is a big challenge to system performance. Let's see how engineers working for MySpace, Facebook and Twitter are doing their job.

Note: all uptime buttons and images below are generated in real-time, you can click on images / baners to see reports with details directly from site-uptime.net.

MySpace

[caption id="" align="aligncenter" width="418" caption="MySpace uptime"]MySpace uptime[/caption]

Facebook

[caption id="" align="aligncenter" width="418" caption="Facebook uptime"]MySpace uptime[/caption]

Twitter

[caption id="" align="aligncenter" width="418" caption="Twitter uptime"]MySpace uptime[/caption]

As you can see the "winner" in this category is: Twitter! ;-)

Site-uptime.net

site-uptime.net is a service that continuously monitors websites and allows to configure email / SMS notifications on downtime. May be helpful for professionals who care about accessibility and uptime of their services.

Watch your HTTPD logs

Recently I observed that AdWords-generated traffic dissapeared from Analytics panel. I thought: WTH?

I checked the logs and saw that URL called by AdWords:

http://my-site.com/?gclid=342343445345....

Generated 403 (Forbidden) server response. That was caused by recent change in Lighttpd filtering rules. I was paying for AdWords traffic but customer hit 403 error page. Ops!

In order to easily spot such problems in future I created the following scanner to easily find all error server responses.

awk '$9>=400' /var/log/lighttpd/access.log | less

If you are boring of 404 errors you can filter them out as well (leaving only 403 / 500 errors for investigation):

awk '$9>=400 && $9 != 404' /var/log/lighttpd/access.log | less

I discovered that the following URLs were inaccessible:

  • /robots.txt (exclusion rules for web crawlers)
  • /favicon.ico (icon used by web browsers)

Next step could be automation of this check (cron job that will send an alert if errant responses count is higher than N). It's left as exercise for the reader.

Custom CacheProvider for Hibernate based on Websphere DistributedMap

Hibernate is a library that maps database tables to Java objects. Is performance problems arise it's very easy to add database caching for application using Hibernate (just few options in config file). Hibernate is shipped with EHCache, default cache implementation. It works well and is easy to setup.

Sometimes you have to use another caching library that has no direct support for Hibernate. Here the Hibernate API comes into play. I'll show you how to plug Websphere's DistributedMap into Hibernate.

First: you have to map get/put requests into desired API. org.hibernate.cache.Cache is an interface that must be subclassed for this task. This class instance is created by Hibernate for every entity that will be cached (distinguished by regionName).

public class WebsphereCacheImpl implements Cache {
    private DistributedMap map;
    private String regionName;
    public WebsphereCacheImpl(DistributedMap map, String regionName) {
        this.map = map;
        this.regionName = regionName;
    }
    public void clear() throws CacheException {
        map.clear();
    }
    public Object get(Object key) throws CacheException {
        return map.get(getMapKey(key));
    }
    public String getRegionName() {
        return regionName;
    }
    public void put(Object key, Object value) throws CacheException {
        map.put(getMapKey(key), value);
    }
    public Object read(Object key) throws CacheException {
        return map.get(getMapKey(key));
    }
    public void remove(Object key) throws CacheException {
        map.remove(getMapKey(key));
    }
    public void update(Object key, Object value) throws CacheException {
    map.put(getMapKey(key), value);
    }
    private String getMapKey(Object key) {
        return regionName + "." + key;
    }
    (...)
}

Then you have to prepare factory for such obejcts:

public class WebsphereCacheProviderImpl implements CacheProvider {

    private DistributedMap distributedMap;

    public WebsphereCacheProviderImpl() throws NamingException {
        InitialContext ic = new InitialContext();
        distributedMap = (DistributedMap) ic.lookup("services/cache/cache1");
    }
    public Cache buildCache(String regionName, Properties arg1) throws CacheException {
        return new WebsphereCacheImpl(distributedMap, regionName);
    }
    public boolean isMinimalPutsEnabledByDefault() {
        return false;
    }
    public long nextTimestamp() {
        return new Date().getTime();
    }
    public void start(Properties arg0) throws CacheException {
    }
    public void stop() {
    }
}

Then new factory class must be registered in Hibernate configuration:

<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="cache.provider_class">com.company.project.cache.WebsphereCacheProviderImpl</property>

And voila!

"Lean Times Require Lean Thinking" by ThoughtWorks

Is Static Typing the Root of All Evil?

Anders Janmyr has written recently an interesting article why he hates static typing: Donald Knuth wrote in his famous paper Structured Programming with go to Statements (PDF) We should forget about small efficiencies, say about 97% of the time: pre-mature optimization is the root of all evil

Since compilation is premature optimization, it is therefore, the root of all evil. Simple! Pretty "smart" reasoning but stupid, IMHO.

He mentioned: "Every time I build my code, my entire code base is type-checked (...) I only care about the method and class that I am currently working on" - yeah, local code change may introduce errors in unexpected parts of a system. I would even use FindBugs if it will improve code quality and slow down total build time. Why? Because I think it's better to allow compiler/lint-tool to control correctness during development process to find most errors as early as possible.

Janmyr assumes the only application of static typing for release preparation (production mode) is better software optimisation. Compiled languages are more efficient than interpreted because many type checks was performed during compile time time and they can be removed from run-time.

I do believe dynamically-typed languages are faster for rapid development (I do love Python, BTW), but there's a hidden cost here. In order to make your code maintainable you have to prepare sufficient level of unit test coverage (I'm using 80% as minumul level for projects in dynamic languages). That's why i'm introducing some kind of static typing by:

I do agree, however, that modern languages are loosing static type-safety by moving configuration outside Java code (XML files, properties, Json) and coding logic in external dynamic sub-languages (JSP). Java gets more type safety from 1.5+ language contructs but most frameworks go in opposite direction.

Isn't Dynamic Typing the Root of All Evil then?

Duck Typing Ducks?

Inbound Marketing by Dharmesh Shah

Dharmesh Shah, Hubspot (and author of the popular On Startups blog) presents "Inbound Marketing" term, mostly how to attract customers to your product using SEO (Search Engine Optimisation) techniques.

He believes "Outbound Maketing" (traditional channels like TV, phones, ...) that gives results in ~1% cases will be replaced by "Inbound Marketing" in future (allow the people to find your product). Dharmesh shows three main channels of "Inbound Marketing":

  • Google (and search engines)
  • Blogs
  • Social Networks (Like Facebook)

commons-logging.jar considered harmfull?

During recent 2nd level cache implementation research I noticed EHCache has a very funny dependency: slf4j. Hey, WTH, yet another log library implementation? - I asked myself. No commons-logging as everywhere?

I googled around and found "The evils of commons-logging.jar and its ilk" article. It highlight some problems related to commons-logging usage:

  1. Different commons-logging versions in one project mirrors DLL-hell problem
  2. Collection logs from all sources into one stream has no bigger value for a developer
  3. Advanced configuration is logging back-end dependant (appenders for log4j for example), so unified layer is not valuable here
  4. Configuration is not intuitive and hard

I agree with 1, 3 and 4. 2 is questionable: sometimes logs sorted in one timeline allows for better error analysis.

slf4j is proposed as an alternative. It's more modular and (probably) simpler that commons-logging. All configuration is a matter of placing selected implementation (slf4j-jdk14-1.5.8.jar for instance) jar on classpath. And voila! - logging is done thru JDK 1.4 logging. Quite simple.