O Hessianu – binárním komunikačním protokolu, který společně s jeho kolegou Burlapem používáme v našich projektech, toho bylo již napsáno hodně. V tomto krátkém článku bych se rád zaměřil na problémy, s kterými jsem se při jeho používání setkal.

Hessian

Pro ty, kteří o Hessianu nikdy neslyšeli nejprve malé shrnutí. Hessian je binární komunikační protokol stavící na principu vystavení rozhraní vaší aplikace a následném volání tohoto rozhraní. Mezi jeho hlavní výhody patří převážně rychlost, snadnost použití, dostupnost pro většinu programovacích jazyků a provázanost s Spring frameworkem.

Pro názornost si ukážeme zcela jednoduchý příklad. Máme následující rozhraní, které chceme vystavit v naší aplikaci:


public interface AppAPI {
    Boolean doSomething(Map params);
}

Toto rozhraní v naší aplikaci naimplementujeme a poté vystavíme pomocí Springu (předpokládáme, že se jedná o webovou aplikaci, kde již běží servlet pro obsluhu požadavků):


    <bean name="/exportedAPI"
          class="org.springframework.remoting.caucho.HessianServiceExporter">
        <property name="service" ref="appAPI"></property>
        <property name="serviceInterface" value="cz.api.AppAPI"></property>
    </bean>

    <bean id="appAPI" class="cz.api.AppAPIImpl"></bean>

Služba bude vystavena na URL /exportedAPI. Následně můžeme v druhé aplikaci, ve které potřebujeme přistupovat k API první aplikace, použít následující konstrukci pro přímou komunikaci:


HessianProxyFactory factory = new HessianProxyFactory();
AppAPI api = (AppAPI) factory.create(AppAPI.class, "http://apprurl.cz/exportedAPI");
Boolean answer = api.doSomething(...);

Jednoduché že? Hessian se postará o zbytek – tedy o samotnou komunikaci, převedení na binární podobu apod.

Problém č.1 – přístup ke komunikaci

S jednoduchostí s jakou se Hessian používá se nese jeden zásadní problém – Hessian se stará o celou komunikaci a není tedy standardně možné dotázat se například na IP adresu protistrany. Naštěstí se tento problém dá celkem elegantně vyřešit.

Stačí nám k tomu přetížit třídu HessianServiceExporter, ve které přetížíme metodu handleRequest. Tímto se dostaneme k objektům HttpServletRequest a HttpServletResponse a máme vyhráno.


public class CustomHessianExporter extends HessianServiceExporter {

    /**
     * Processes the incoming Hessian request and creates a Hessian response.
     */
    public void handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        if (!"POST".equals(request.getMethod())) {
            throw new HttpRequestMethodNotSupportedException(request.getMethod(),
                    new String[]{"POST"}, "HessianServiceExporter only supports POST requests");
        }

        try {
            // TODO - uděláme něco s requestem - například ho můžeme uložit do ThreadLocal proměnné
            invoke(request.getInputStream(), response.getOutputStream());
        }
        catch (Throwable ex) {
            throw new NestedServletException("Hessian skeleton invocation failed", ex);
        }
    }

Problém č.2 – read / connection timeout

Další nemalý problém při používání knihovny jsem objevil při nastavování timeoutů. Tento problém není ani tak problémem samotného Hessianu, ale spíše Springu, který neumožňuje nastavit connection timeout (chybí podpora pro metodu setConnectionTimeout – viz zde).
Pokud použijete přímo Hessian bez Springu, nebude s tímto problém (od verze 4.0.3 – do té doby podpora také chyběla).

Jaký je rozdíl mezi zmiňovanými timeouty:

  • connection timeout – slouží k určení doby, po kterou se budeme snažit připojit k dané službě předtím, než proběhne samotná komunikace
  • read timeout – slouží k určení doby, po kterou se budeme snažit číst odpověď dané služby

Vrátíme-li se k příkladu vytváření HessianProxyFactory, tak můžeme nastavit read/connection timeout bez sebemenších problémů:


HessianProxyFactory factory = new HessianProxyFactory();
factory.setReadTimeout(timeout);
factory.setConnectTimeout(timeout); // od verze 4.0.3
...

Použijete-li přímo třídu od Springu – HessianProxyFactoryBean, tak v tomto případě nastavení connection timeoutu není možné – závisí tedy pouze na síťové vrstvě, jak dlouho budeme čekat na spojení.

Pokud musíte používat starší verzi Hessianu, či factory bean od Springu, jedinou cestou pro nastavení příslušného timeoutu zůstává přetížení třídy HessianFactoryBean a úprava metody, která se stará o vytvoření připojení:


...
  protected URLConnection openConnection(URL url)
    throws IOException
  {
    URLConnection conn = url.openConnection();

    conn.setDoOutput(true);

    if (_readTimeout > 0) {
      try {
	conn.setReadTimeout((int) _readTimeout);
      } catch (Throwable e) {
      }
    }

   // TODO přidat nastavení connection timeoutu
...

Závěr

Ačkoliv bychom u Hessianu našli i další problémy či slabosti, jedná se stále o velmi oblíbený a používaný protokol (věřím že i mimo svět Javy). Společně s jeho bratříčkem Burlapem (protokol komunikující pomocí XML) mohou výrazně usnadnit práci tam, kde bychom museli složitě psát vlastní velice podobná řešení.
Napište nám do komentářů vaše případné zkušenosti s tímto protokolem a můžete se podělit i o problémy, na které jste narazili vy.