EL hack …

… aneb jak volat methodu s parametrem pomocí Expression Language.

Možná, že je to tím že jsem si dostačně pořádně nepřečetl dokumnetaci k EL, ale nějak jsem nezjistil jak zavolat methodu objektu s parametrem a párkrát by se mi to hodilo. V EL přímo to asi nejde, ony na to budou nejspíš budou existovat nějak tag library, ale ty se mi většinou nelíbí, protože občas je dost horor k nim najít dokumentaci. Proto jsem přemýšlel jak to udělat bez tld přímo pomocí EL.
Určitě to není uplně nejčistější řešení a určitě nejsem jediný koho to napadlo, ale zajímal by mě váš názor na tento Hack.

Takže o co de. V podstatě jde o využití operátoru [], který EL využívá takto:

The JSP expression language unifies the treatment of the . and [] operators. expr-a.identifier-b is equivalent to expr-a[„identifier-b“]; that is, the expression expr-b is used to construct a literal whose value is the identifier, and then the [] operator is used with that value.

To evaluate expr-a[expr-b], evaluate expr-a into value-a and evaluate expr-b into value-b. If either value-a or value-b is null, return null.

  • If value-a is a Map, return value-a.get(value-b). If !value-a.containsKey(value-b), then return null.
  • If value-a is a List or array, coerce value-b to int and return value-a.get(value-b) or Array.get(value-a, value-b), as appropriate. If the coercion couldn't be performed, an error is returned. If the get call returns an IndexOutOfBou­ndsException, null is returned. If the get call returns another exception, an error is returned.
  • If value-a is a JavaBeans object, coerce value-b to String. If value-b is a readable property of value-a, then return the result of a get call. If the get method throws an exception, an error is returned.

Já jsem využil první bod, podle kterého vrátím object třídy implementující rozhraní Map, této třídě jenom přepíšu methodu public Object get(Object arg0). Takže EL výraz ${myBean.method­Name[paramValu­e]} zavolá myBean.getMet­hodName().get(pa­ramValue); což je v důsledku volání methody s parametrem Object.

Zde je ukázka kontrétní implementace takového beanu (teď ale nevím jestli se tomu ještě může říkat java bean):

package com_3rojka.demo.EL.hack;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

public class MyBean {

        private String greeting;
        private GreetingParamMapper greetingPM;

        private class GreetingParamMapper implements Map {

                private MyBean myBean;

                public GreetingParamMapper(MyBean myBean) {
                        this.myBean=myBean;
                }

                public Object get(Object arg0) {
                        return myBean.getGreeting() + " " + arg0;
                }

                public boolean containsKey(Object arg0) {
                        return true;
                }

                /* ... other unimplemented methods ... */

        }

        public MyBean(String greeting) {
                this.greeting = greeting;
                this.greetingPM = new GreetingParamMapper(this);
        }

        public String getGreeting() {
                return greeting;
        }

        public GreetingParamMapper getGreetingWithName() {
                return greetingPM;
        }
}

A zde je ukázka myBean používajícího jsp:

<%@page import="com_3rojka.demo.EL.hack.MyBean"%>
<%
request.setAttribute("myBean", new MyBean("Hello"));
%>
<html>
  <body>
    <h2>${myBean.greetingWithName["World"]}</h2>
  </body>
</html>

Celé demo si lze stáhnou EL-hack-demo.zip jako maven project. Po rozbalení lze spustit webapp pomocí >mvn jetty:run.


8 komentáře k článku “EL hack …”

  1. Cyril říká:

    tuto metodu spokojene pouzivam uz par let. Jeste bych doporucil si udelat spolecneho predka takovychto Map, v kterem by se implementovaly ostani nezazivne metody, ktere vraci null a nikdy se nepouziji…

  2. Honza Novotný říká:

    No já jsem si v takových případech vždy vystačil se scriptletem. Faktem je, že mnoha lidem scriptlety v JSP vadí – snižují přehlednost. Nicméně u expression scriptletů () si myslím tomu tak není.

  3. Jan Vršinský říká:

    Zajímavý nápad v článku. V důsledku mi řešení ale nepřijde moc šťastné, neboť vyžaduje přípravu Java kódu pro to, aby ho šlo volat z EL.

    Scriptlety mají zase způsobují problémy zkombinované s iteračními a větvícími tagy.

    Systém, které používám já, je vytvoření vlastního tagu pro volání metod. Když tag vytvořím pomocí JSP2.0, nemusím řešit TLD a stačí ho umístit do WEB-INF.


    Výsledné volání //jakékoliv// metody z //jakékoliv// třídy potom vypadá tak, jak je uvedeno níže.

    >
    >
    > … zde inicializace modelu
    >
    >

    Má tag library se jmenuje model, protože jsem ji vytvořil v rámci jednoho prototypového projektu, ve kterém jsem celý MVC pattern implementoval pomocí JSP2.0 a EL (pro možnost online editace funkčnosti), takže v JSP controllerech jsem potřeboval volat metody modelů.

    Počet parametrů tagu je dynamický od 0 do 99, to se v tag souboru dělá pomocí

    Samotná metoda se zavolá přes reflection API. Tag má dva nepovinné parametry, result pro možnost uložení výsledku do atributu requestu a paramCount pro specifikování počtu parametrů. Počet parametrů je nutný specifikovat v případě, kdy chci volat metody s např. 5 parametry a poslední tři z nich musí být prázdné stringy. EL by to potom bral, jako kdyby parametry byly jen dva a zavolal by jinou metodu.


    Pokud máte zájem, tak zdroják toho tagu je tady:
    www.janvrsinsky­.com/java/call­.zip

    Při jeho vytváření jsem vycházel z článku
    http://www.ja­vaworld.com/…ca­lltag.html?…
    a ještě z nějakého tutorialu k JSP2.0, bohužel už nevím.

    Těším se na zpětnou vazbu a na další názory k tomuto tématu. Jak to řešíte vy?

  4. 3rojka říká:

    JSP Tagy jsou taky dobrá možnost, mě to sice zrovna tohle připadlo složitější, ale jinak dobré. Taky asi zaleží na co to člověk potřebuje a pak si může vybrat.
    Jinak já jsem tu svoji metodu zatím, nikdy nepoužil jenom mě to tak napadlo a zatím se mi to nezdá jako špatný nápad.

  5. Jan Vršinský říká:

    Formátování tady funguje jinak než testovací formátování Texty!, tak se v mém příspěvku nezobrazilo vše, pro jistotu tedy bez ohraničujících závorek:

    pouziti tagu
    %@ taglib prefix=”model” tagdir=”/WEB-INF/tags/model” %

    volani metody
    model:call model=”myModel” method=”myMethod” p1=”hello” p2=”0.5” /

  6. 3rojka říká:

    Budu muset něco udělat s tou Texy, ale jinak jsem se na do díval do toho zdrojáku, takže se to dalo pochopit.

  7. Lukáš Vlček říká:

    Škoda, že teď nejsem týden v praci, kdyby ses stavil, tak bych ti řekl, že jsem se s touto potřebou už setkal a nakonec se jako nejšťastnější vždy ukázal JSP Custom Tag (není třeba nic hackovat). Tedy, pokud jsem pochopil dobře podstatu tvého problému. Ostatně myslím, že Tom by ti to také doporučil. Né, že by tvoje řešení postrádalo kreativitu, ale myslím, že se může stát, že nemusí být uplně vždy dobře čitelné (k tomu, aby čtenář pochopil, co se opravdu dějě musí zkoumat i implementaci tvojí Mapy a nemá nikdy jistotu, že v různých projektech nebudeš používata různé implementace s odlišným chováním).

  8. 3rojka říká:

    S custom tagem jsem zde už jednou souhlasil, ale jinak jsi mne nepřesvědčil.
    Nevím proč bych v různých projektech implementoval jinak methodu getGreetingWit­hName(), resp methodu get třídy GreetingParam­Mapper, která je implementace privatní třídy mého beanu. A s tím čtením implementace bych řekl, že to musím vždycky pokud nemáš popsaný interface takže tady taky nevidím rozdíl.
    Takže správně by to vypadalo takhle:

    /**
     * mappuje EL vyraz ${myBean.greetingWithName[name]}
     * a vrací pozdrav uživateli se jmenem name
     */
    public GreetingParamMapper getGreetingWithName()

    A kdyby GreetingParamMapper byl potomkem nějaké abstaktní třídy, třeba ParameterMapper (což jsem zda nedělal jenom pro přehlednost příkladu) tak by ti to ani nepřišlo divné.

    Ale jinak máš pravdu je to hack.

Zanechte komentář

Můžete použít Texy! formátování.

Vložte text na obrázku: