Archív

Archív pro

JSF and nice URLs - PrettyFaces

In this article I would like to introduce URL rewriting engine for JSF – PrettyFaces and provide a maven archetype for quick startup.

The motivation for using rewriting engine in web development is straightforward – nice URLs are better for SEO and easier to remember.

Integration of PrettyFaces

Integration of PrettyFaces in existing project is well described in PrettyFaces documentation. Step are following:

  1. Download PrettyFaces binary and all dependencies and put them in classpath of your project.
  2. Define PrettyFaces filter in /WEB-INF/web.xml.
  3. Create config file /WEB-INF/pretty-config.xml file.

That`s all! Now you can start to use it.

URL mapping example

<url-mapping id="viewItem">
        <pattern>/item/#{bean.selectedItem}/</pattern>
        <view-id>/pages/viewItem.jsf</view-id>
</url-mapping>

The URL mapping example above causes that any request in form /item/[anything here]/ will be served by /pages/viewItem.jsf page. Value of #{bean.selectedItem} expression will be evaluated during runtime (getter of selectedItem property of managed bean bean will be triggered).

On the other side – there is a simple way how to generate this link on JSF template in consistent way. In other words – there is only one place with rewriting rules. Once you change it there it is changed all across the project. There are two components made for this purpose:

  • pretty:link (similar as c:url but id of url mapping is used to refer the URL)
  • pretty:urlbuffer (similar as pretty:link but stores the URL in a variable instead of printing it immediatelly)
<pretty:link mappingId="viewItem">
        <f:param value="#{item}" />
        <h:outputText value="#{item}" />
</pretty:link>

There are two more <url-mapping> subelements I would like to mention:

  • <query-param name=""></query-param> – let you specify query parameter and method that will return its value.
  • <action phaseId="" onPostback=„fal­se“></action> – let you specify the bean a method that will be called during specified Phase (default phase is RESTORE_VIEW). Another parameter is onPostback that says if method should be triggered during postback.

Navigation

Instead of navigation rules stored in faces-config.xml you can use PrettyFaces to navigate. There is a special prefix pretty: you can use in method of managed beans. In case you return pretty:home view id PrettyFaces will search for url mapping with id home and redirect to this page. Or you can use just pretty: to reditect to current page.

PrettyFaces archetype

As a base for my archetype I choose Deltaset maven archetype which already includes JSF, Facelets and Richfaces. But there was one tricky problem – a dependency on ` xerces:xercesIm­pl:jar:2.4.0` which causes a SaxParser exception during Jetty start. This library was referenced from nekohtml. The solution was quite simple – exclude old version of xercesImpl and add a depencency on newer one.

Steps to create project on your computer

It is supposed you have Maven installed on your computer.

  1. Download and unzip PrettyFaces maven archetype.
  2. Install archetype into your local repository. Call mvn install in folder where you unzipped download archetype.
  3. Go to folder where you wanna create new project and call (replace [group-id] and [artifact-id] with real values)
mvn archetype:generate \
-DarchetypeGroupId=cz.itplace \
-DarchetypeArtifactId=archetype-prettyfaces \
-DarchetypeVersion=1.0-SNAPSHOT \
-DgroupId=[group-id] \
-DartifactId=[artifact-id]

Now call mvn jetty:run to start Jetty container. Or mvn eclipse:eclipse to generate an Eclipse project.

Conclusion

PrettyFaces is nice piece of code. Easy to understand and well documented.

Links

Vlastimil Vávrů Articles in english

Effective search under a subtree

Almost every blogging system supports assigning articles into categories and subcategories. Listing articles which belong to a subtree of categories may be quite expensive operation especially if the number of articles is very high. In this article I would like to describe one of possible solutions.

Category Tree

Tree of article categories could look something like:

  • Music
    • Rock
    • Pop
    • Jazz
  • Sport
    • Snowboarding
    • Skiing
      • Cross-country skiing
      • Downhill skiing
    • Kiteboarding
  • Traveling
    • Adventure
    • Leisure

The most simple way of assigning an article to a category would be to give each category an id (e.g. Music=1, Rock=2, Pop=3, Jazz=4, Sport=5,…) and add categoryId column into Article database table. Then if we wanted to get all articles from category Rock we could do it simply by running something like:

select * from Article where categoryId=2

But getting all articles from all subtree under Music category will be more complicated, we have to make list of category ids from all Music subtree and query something like:

select * from Article where categoryId in (1,2,3,4)

Imagine much larger category tree, the number of categories in a subtree may be high. We wanted something more efficient and still we would like to be able to add new categories into a tree.

Limited tree

In our project the efficiency was really top priority, therefore we were able to accept slight limitations to our category tree which could give us better performance:

  • Maximum tree depth is defined once and then constant. Let's name this parameter maxLevels.
  • Maximum number of children a parent may have is defined once and then constant. This parameter can be called maxChildren.

Limited tree evaluation

Position of every tree node can be described by path to it from the tree root. Look at following tree:

  • Music {1}
    • Rock {1,1}
    • Pop {1,2}
    • Jazz {1,3}
  • Sport {2}
    • Snowboarding {2,1}
    • Skiing {2,2}
      • Cross-country skiing {2,2,1}
      • Downhill skiing {2,2,2}
    • Kiteboarding {2,3}
  • Traveling {3}
    • Adventure {3,1}
    • Leisure {3,2}

Path can have maximally maxLevels items and each item in path is in range from 1 to maxChildren. For simplification we filled paths shorter then maxLevels by 0s. So for example if maxLevels = 6 then path to Pop node is {1,2,0,0,0,0}. Then we can express a path as int array of size maxLevels.

Now for each node we can calculate a single integral value using this equation (the operator ^ means power):

value(path) = path[0]*(maxChildren+1)^(maxLevels-1)
 + path[1]*(maxChildren+1)^(maxLevels-2)
 + path[2]*(maxChildren+1)^(maxLevels-3)
 + ...
 + path[maxLevels-1]*(maxChildren+1)

Does this equation resemble something to you? Yes you have seen it before, it is equation for conversion a number from numeric system with base (maxLevels+1) into decimal system.

Into Article table we add new column categoryValue into which we will put the calculated value based on a category an article belongs to.

Example

Now why we did all this? Let me show you on example. The tree we will use looks the same as above. At first we have to define maxLevels and maxChildren parameters. Let's define maxLevels=4 and maxChildren=7 (When evaluating tree nodes we will therefore convert number from octal system into decimal.)

We have written a new article and we want to assign it to category Skiing (path is {2,2} which is equal to {2,2,0,0}). So we can calculate the categoryValue column value using equation defined above:

value(Skiing {2,2,0,0}) = 2*8^3 + 2*8^2 + 0*8^1 + 0*8^0 = 1024 + 128 = 1152

Then let's say we want to list all articles from category Sport {2} and below. It means everything between Sport {2} inclusive and Traveling {3} (which is next sibling to Sport) exclusive. So we calculate lower and upper limits:

lower = value(Sport {2,0,0,0}) = 2*8^3 + 0*8^2 + 0*8^1 + 0*8^0 = 1024
upper = value(Traveling {3,0,0,0}) = 3*8^3 + 0*8^2 + 0*8^1 + 0*8^0 = 1536

The sql to get all articles from under the Sport node is then:

select * from Article where categoryValue >= :lower and categoryValue < :upper

It looks more complicated then it really is. For testing we use maxChildren=9 because then paths form direcly decimal numbers. For example when maxLevels=5 then:

value(2,3,4) = value(2,3,4,0,0) = 23400

Real life

Tree parameters are limited by type we store calculated value in. We used long java type which is 64 bits type. We set maxLevels=6 and maxChildren=10. It is sufficient for our needs and very fast.

Jan Šmuk Programming

OAuth - představení autentizačního protokolu

V dnešním článku popíšu princip fungování autentizačního protokolu OAuth a důvody pro jeho použití.

K čemu je OAuth?

Součástí jednoho našeho projektu byla integrace se sociálními sítěmi (synchronizace kontaktů, statusů přátel, zpráv, multimediální galerie). V našem případě to byly: Facebook, Twitter, Gmail, MSN a YouTube. Všechny uvedené servery mají veřejné API, pomocí kterého lze k jeho funkcím přistupovat. V prvních prototypech jsme v našich konektorech pro autentizaci používali uživatelské jméno heslo (používala se HTTP basic autentizace. V reálné aplikace není z různých důvodů (bezpečnost) možné uživatelské jméno a heslo ukládat do databáze. A právě k tomu je OAuth.

OAuth autentizace umožňuje uživatelům sdílet data, texty, fotografie a videa uložená jednom serveru, s jiným serverem, aniž by museli vyzradit svoje přístupové údaje.

Jak OAuth funguje?

Nejdříve si představíme základní pojmy:

  • Service provider – služba, která obsahuje chráněné zdroje a má být chráněna. Může se jednat o bankovní server, mikroblogovací server, nebo server pro správu obrázků.
  • Uživatel – reálný uživatel služby (Service provideru), vlastník chráněného obsahu.
  • Consumer – aplikace přistupující k chráněným zdrojům uživatele. Zdroje leží na service provideru.
  • Chráněný zdroj – obsah uživatele (data, texty, obrázky, videa), který chce chránit.

Nejdříve je nutné consumer u service providera zaregistrovat. Způsoby registrace jsou různé. Nejčastěji je to webové rozhraní. Rovnou uvedu příklad – zvolím si Twitter. Mějme aplikaci, která se bude připojovat na Twitter (tato aplikace je consumer) a bude si stahovat naše nejnovější zprávy.

V případě Twitteru se přidá (zaregistruje) nová aplikace zde – Register an Application (pro přidání aplikace musíte mít účet na Twitteru a být přihlášeni). Pro ty z vás co nemají účet na Twitteru uvedu přehled nejdůležitějších vlastností, které je možné u aplikace vyplnit:

  • jméno, popis, domovská stránka aplikace
  • callback URL (adresa, na kterou bude uživatel přesměrován po nastavení práv)
  • typ přístupu (read only, read and write)
Naše aplikace v Twitteru

Naše aplikace v Twitteru

Parametry aplikace nejsou povinné. Ty, jež jsem uvedl, platí pouze pro Twitter a nejsou součástí specifikace OAuth. V jiné aplikace může být např. granularita přístupových práv mnohem jemnější (např. přístup pouze do určitého adresáře s fotkami a na omezenou dobu).

Výsledkem registrace consumera je consumer key a consumer secret. Consumer key identifikuje consumera. Consumer secret slouží pro podepisování požadavku.

1) Získání request tokenu

Uživatel je na webu consumera a klepne na tlačítko (odkaz) pro získání přístupu. Odkaz vede na web consumera a provede se následující:

Získání request tokenu – provede se na serverové straně consumera (např pomocí HttpClient nebo standardní HttpURLConnec­tion. Na straně consumera je service provider identifikován třemi URL:

HTTP request z consumera na service provider musí být podle OAuth standardů. Musí obsahovat povinné parametry (ty musí být správně encodovány) a být podepsán.

Povinné parametry:

  • oauth_consumer_key: The Consumer Key.
  • oauth_signatu­re_method – typ šifrování použitý pro signature (podpis) requestu (PLAINTEXT, HMAC-SHA1, RSA-SHA1)
  • oauth_signature – podpis zprávy (postup vytvoření podpisu bude popsán)
  • oauth_timestamp – klasický Unix timestamp označující dobu vytvoření requestu
  • oauth_nonce – unikátní číslo requestu
  • oauth_version – verze (nepovinný parametr). Současná hodnota je 1.0.

Vytvoření podpisu (hodnoty parameru oauth_signature):

  1. Shromáždí se veškeré parametry (kromě parametru „realm“) – query parametry, POST parametry, parametry z OAuth hlavičky
  2. Parametry se převedou do kódování UTF-8 (kromě binárních dat) a provede se URL encodování
  3. Parametry jsou seřazeny podle abecedy a spojeny v jeden řetězec
  4. Z řetězce se pomocí consumer secret a token secret vygeneruje podpis a přidá se jako parametr oauth_signature do requestu

Výsledný request:

GET /oauth/request_token HTTP/1.1
Host: twitter.com:80
Authorization: OAuth realm="http://twitter.com/oauth/request_token"
    oauth_consumer_key="asdfasqeiou235sd"
    oauth_token=""
    oauth_nonce="2345667878"
    oauth_timestamp="1230768000"
    oauth_signature_method="HMAC-SHA1"
    oauth_version="1.0"
    oauth_signature="4TbFhc%2FDKyGswotg39339AR4J5k%3D"

Výsledkem zavolání requestu je request token a request token secret.

2) Přesměrování na stranu service providera

Uživateli je vrácen response, který v hlavičce přesměrovává na URL pro autentizaci uživatele na straně service providera. V případě Twitteru je to adresa http://twitter.com/oauth/authorize. Jako parametry jsou poslány request token (získaný v předchozím kroku) a volitelně callback URL (adresa pro přesměrování po nastavení přístupových práv na straně service providera).

3) Nastavení přístupových práv

Na straně service providera se uživatel přihlásí a povolí consumerovi přistupovat k aplikaci. O různých typech přístupových práv jsem mluvil dříve v článku.

4) Přesměrování zpět na consumera

Následuje přesměrování zpět na consumera na zadanou callback URL. Jako parametr je poslán request token, aby consumer věděl, k čemu vlastně byla práva nastavena.

5) Consumer požádá o access token

Consumer na straně serveru vytvoří HTTP request stejně jako v bodu 1 při získávání request tokenu. Rozdíl je v cílové URL service providera – nyní je to URL pro výměnu request token za access token. Pro Twitter je to http://twitter.com/…access_token. Když je přístup pro daný request token povolen, je navrácen access token a access token secret. Tím je celý proces dokončen.

Access token je součástí OAuth hlavičky, access token secret se používá současně s consumer secret pro podepsání requestu.

Předchozích 5 bodů musí uživatel v ideálním případě obsolvovat pouze jednou. Záleží na typu aplikace – někdy se nastavuje přístup na omezenou dobu. Pak záleží pouze na tom, zda service provider po nějaké době access token nevyexpiruje. Podle našich zkušeností to ale tak nevypadá. Např. Google tvrdí, že access token neexpiruje nikdy.

Přístup consumera k chráněnému zdroji

Nyní může consumer přistupovat k chráněným zdrojům. Abyste dostali lepší představu co mohou být chráněné zdroje, a jak k nim přistupovat, podívejte se na Twitter API. Např. pomocí metody http://twitter.com/friends/ids.xml můžete dostat všechny přátele vybraného uživatele.

Jak je to s podporou OAuth u sociální sítí a dalších webů?

Přímo na domovském webu OAuth je možné se dočíst, že jej podporují následující weby: Digg, Jaiku, Flickr, Ma.gnolia, Plaxo, Pownce, Twitter, and hopefully Google, Yahoo. Článek, který informace obsahuje je téměr 2 roky starý, takže nyní bude situace ještě o mnoho lepší. Např Twitter, který má OAuth podporu stále jenom v beta provozu, tvrdí, že po jeho ostrém spuštění po nějaké době zruší možnost autentizace pomocí HTTP basic authentication. Cituji:

Can my application continue to use Basic Auth?
There is no requirement to move to OAuth at this time. If/When a date is set for the deprecation of Basic Auth we will publish a notice on the API Development Talk. We will not set a date for deprecation until several outstanding issues have been resolved. When we do set a date we plan to provide at least six months to transition.

Rozdíl mezi OAuth a OpenID

OpenID neskrývá identitu uživatele. Slouží pouze k jejímu ověření. Zatímco pomocí OAuth umožňuje přístup ke zdrojům bez přímého udání identity uživatele (uživatelského jméne, hesla a další osobních údajů). Každý z autentizačních mechanizmů se proto hodí pro různé použití. Bez zajímavosti není, že oba protokoly mají společné autory.

Závěr

OAuth je dnes již standardním způsobem autentizace, který používají největší hráči na trhu (Google, Yahoo, YouTube, Twitter). Čas investovaný do jeho porozumění a implementace se rozhodně vyplatí.

Odkazy

Vlastimil Vávrů Internet obecně

Jak si JBPM rozumí se Springem

09.04.2009

V posledním článku Jak jsme použili JBPM jsem popsal, jak a proč jsme použili JBPM. Dnes se podělím o naše zkušenosti s integrací se Springem.

Spring a JBPM se v jednom z projektů, kde jsme oba použili, stýkají ve dvou místech:

  1. Konfigurace – můžeme například potřebovat, aby JBPM použil pro svůj běh již existující nakonfigurovanou hibernate session factory, kterou používá i zbytek naší aplikace.
  2. Spouštění akcí – chceme, aby akce byly součástí našeho spring kontextu.

Spring Modules

Začali jsme tím, že jsme použili springmodules-jbpm31, který již řeší oba výše zmíněné body. Takto může vypadat kousek definice spring kontextu:

...
<!-- jBPM configuration -->
<bean id="jbpmConfiguration"
    class="org.springmodules.workflow.jbpm31.LocalJbpmConfigurationFactoryBean">
  <property name="sessionFactory" ref="sessionFactory" />
  <property name="configuration" value="classpath:jbpm.cfg.xml" />
  <property name="createSchema" value="false" />
  <property name="processDefinitionsResources">
    <list>
      <value>classpath:createArticle.xml</value>
      <value>classpath:updateArticle.xml</value>
    </list>
  </property>
</bean>
...

Jak springmodules řeší druhý bod, tedy spouštění akcí? Podlě mě ne příliš šikovně. Představme si akci, která posílá uživateli email. Chceme použít Ioc k nastavení reference na objekt, který bude obstarávat posílání emailů. Springmodules toto řeší použitím bean proxy, které řekneme id našeho action handleru ve spring kontextu. Deklarace takové akce pak vypadá nějak takto:

...
<action name="myAction" config-type="bean"
   class="org.springmodules.workflow.jbpm31.JbpmHandlerProxy">
 <targetBean>sendEmailActionHandler</targetBean>
 <factoryKey>jbpmConfiguration</factoryKey>
</action>
...

Všimněte si atributu config-type. Pokud je jeho hodnota bean, pak JBPM při vykonávání akce vytváří novou instanci třídy specifikované v atributu class. Poté nastavuje této instanci její vlastnosti na základě jmen a obsahů vnořených tagů. Tedy ve výše uvedeném případě je vytvořena nová instance třídy org.springmodules.workflow.jbpm31.JbpmHandlerProxy a jsou na ni zavolány metody setTargetBean("sendEmailActionHandler") a setFactoryKey("jbpmConfiguration"), což jsou vlastnosti nutné k fungování této proxy.

Malý trik

Zdá se vám tento způsob složitý? Mi se zdál složitý až zbytečně moc. Jednak takto sestavené akce hyzdí definici procesu. Dále jste si jistě všimli, že zde chybí definice těla posílaného emailu a adresa příjemce. Nenašel jsem lepší způsob, jak mít tyto dvě hodnoty přímo v definici procesu, než je ukládat jako procesní proměnné. Proto jsem hledal způsob, jak být schopen definovat akci ve stylu:

...
<action name="myAction" config-type="bean"
   class="itplace.jbpm.SendEmailActionHandler">
 <emailAddress>recipient@server.com</emailAddress>
 <emailBody>hello</factoryKey>
</action>
...

Samotná akce pak bude vypadat takto:

package itplace.jbpm;

import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;

public class SendEmailActionHandler implements ActionHandler {
  private EmailSender emailSender;
  private String emailAddress;
  private String emailBody;

  public void execute(ExecutionContext executionContext) throws Exception {
    emailSender.send(emailAddress, emailBody);
  }

  public void setEmailSender(EmailSender emailSender) {
    this.emailSender = emailSender;
  }

  public void setEmailAddress(String emailAddress) {
    this.emailAddress = emailAddress;
  }

  public void setEmailBody(String emailBody) {
    this.emailBody= emailBody;
  }

}

K identifikací našich akci ve spring kontextu jsme použili konvenci pojmenovávání:

  • pokud je hodnota atributu class například itplace.jbpm.SendEmailActionHandler, pak budeme ve spring kontextu hledat bean s id sendEmailActionHandler – tedy jednoduché jméno třídy s malým písmenkem na začátku. Definice naši akce ve spring kontextu tedy bude:
...
<bean id="sendEmailActionHandler" class="itplace.jbpm.SendEmailActionHandler"
    singleton="true">
  <property name="emailSender" ref="emailSender"/>
</bean>
...

Nyní potřebujeme JBPM donutit, aby nevytvářelo novou instanci třídy akce, ale aby se použil bean definovaný ve spring kontextu s identifikací odpovídající konvenci. Poté už JBPM může standardně nastavit vlastnosti emailAddress a emailBody.

JBPM si ve třídě Delegation drží statickou mapu objektů typu Instantiator. Instantiator je zodpovědný za vytváření instancí akcí. Klíčem v této mapě je String a JBPM v této mapě hledá Instantiator podle obsahu atributu config-type. Standardně je pod klíčem bean v mapě uložený BeanInstantiator. Mým cílem bylo tento rozšířit.

Nejprve jsem si vytvořil třídu, která mi umožní obecně manipulovat s mapou instantiatorů:

package itplace.jbpm;

import java.util.Map;
import org.jbpm.instantiation.Delegation;
import org.jbpm.instantiation.Instantiator;

public class AdditionalInstantiatorsDeclaration extends Delegation {
  public void setInstantiators(Map<String, Instantiator> instantiators) {
    instantiatorCache.putAll(instantiators);
  }
}

instantiatorCache je ona zmíněná statická mapa, do které nyní můžeme přidat libovolný instantiator pod libovolným jménem. Náš nový instantiator vypadá nějak takto:

package itplace.jbpm;

import org.jbpm.instantiation.BeanInstantiator;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

public class SpringInstantiator extends BeanInstantiator
      implements BeanFactoryAware {
    private BeanFactory beanFactory;

    protected Object newInstance(Class clazz) {
        String beanName = clazz.getSimpleName();
        beanName = beanName.substring(0, 1).toLowerCase()
          + beanName.substring(1);
        if (beanFactory.containsBean(beanName)) {
            return beanFactory.getBean(beanName);
        }

        return super.newInstance(clazz);
    }

    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }
}

SpringInstantiator implementuje rozhraní BeanFactoryAware, takže se spring postará o vložení bean factory. A pak jsme rozšířili standardní BeanInstantiator a změnili chování metody newInstance, která nyní nejprve hledá v bean factory bean podle konvence a pokud jej nenajde pouze vytvoří novou instanci.

Do spring kontextu je tedy zapotřebí přidat následující:

...
<bean id="additionalInstantiatorsDeclaration"
    class="itplace.jbpm.AdditionalInstantiatorsDeclaration">
  <property name="instantiators">
    <map>
      <entry>
        <key><value>bean</value></key>
        <bean class="itplace.jbpm.SpringInstantiator" />
      </entry>
    </map>
  </property>
</bean>
...

Je ještě vhodné říct springu, že JBPM je závislý na beanu additionalInstantiatorsDeclaration, stačí přidat depends-on atribut do JBPM konfigurace:

...
<!-- jBPM configuration -->
<bean id="jbpmConfiguration" depends-on="additionalInstantiatorsDeclaration"
    class="org.springmodules.workflow.jbpm31.LocalJbpmConfigurationFactoryBean">
...

Závěr

Viděli jste, jak jednoduše lze JBPM integrovat se Springem. Také jste si možná udělali obrázek o zdařilosti a praktičnosti spring jbpm modulu. Způsob, který jsem vám předvedl, naprosto vyhovuje našim požadavkům a funguje opravdu velmi dobře.

Zdroje:

Jan Šmuk Java , ,