Looks good. If you have time for it couldn't you turn this into a wiki? In that way it's easier for users to find this.
http://www.wicket-wiki.org.uk/wiki
Frank
On 10/3/06, Leszek Gawron <[EMAIL PROTECTED]> wrote:
Hello,
I just wanted to share another way of injecting spring services into
wicket code. This one uses AOP.
- o - Why another approach? - o -
Using wicket-spring along with wicket-spring-annot works nicely for
components (althought you have to remember not initializing it yourself)
but does not work for other parts of application - models. Just ask your
self how many times you have put a spring service into wicket page only
to pass it to model constructed:
> public class RankingPanel extends Panel {
> @SpringBean
> private LeagueService leagueService;
>
> public RankingPanel( String id, IModel leagueModel ) {
> super( id );
> add( new ListView( "ranking", new RankingModel( leagueService, leagueModel ) ) {
> @Override
> protected void populateItem( ListItem listItem ) {
> PlayerRank rank = (PlayerRank) listItem.getModelObject();
> listItem.add( new Label( "position", String.valueOf( rank.getPosition() ) ) );
> listItem.add ( new Label( "player", rank.getPlayer().getFullName() ) );
> }
> } );
> }
> }
If you could have your model injected with appropriate service this
would probably be:
> public class RankingPanel extends Panel {
> public RankingPanel( String id, IModel leagueModel ) {
> super( id );
> add( new ListView( "ranking", new RankingModel( leagueModel ) ) {
> @Override
> protected void populateItem( ListItem listItem ) {
> PlayerRank rank = (PlayerRank) listItem.getModelObject();
> listItem.add( new Label( "position", String.valueOf( rank.getPosition() ) ) );
> listItem.add( new Label( "player", rank.getPlayer().getFullName() ) );
> }
> } );
> }
> }
- o - What you'll need - o -
- your current fancy project
- one fresh spring (at least 2.0 M1). I have used 2.0-rc2.
- one ripe aspectj (http://ibiblio.org/maven2/aspectj/ ) especially
aspectjrt and aspectjweaver. I have used 1.5.2.
- java 1.5 (there are probably some ways to do it with 1.4 - didn't
bother to try)
- o - Implementation - o -
Spring 2.0 M1 introduced @Configurable annotation [1]. We can use it to
inject spring beans into ANY object (not only created by spring but also
simply instantiated with new MyObject() ).
Let's create a simple spring context:
> <?xml version="1.0" encoding="UTF-8"?>
> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance"
> xmlns:aop="http://www.springframework.org/schema/aop " xmlns:tx="http://www.springframework.org/schema/tx"
> xsi:schemaLocation="
> http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
> http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
> http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
> <bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor "/>
>
> <aop:spring-configured/>
>
> <bean id="leagueService" class="com.mobilebox.squasher.service.impl.LeagueServiceImpl autowire="byName"/>
> </beans>
We have a single service declared here. What's more important is
<aop:spring-configured/> which simply saying turns on Spring AspectJ
machinery. Please refer to [1] for additional info.
Now let's build a wicket model:
> @Configurable(autowire = Autowire.BY_NAME, dependencyCheck = true)
> public class RankingModel extends LoadableDetachableModel {
> protected transient LeagueService leagueService;
>
> public void setLeagueService( LeagueService leagueService ) {
> this.leagueService = leagueService;
> }
>
> public RankingModel( IModel master ) {
> this.master = master;
> }
>
> @SuppressWarnings("unchecked")
> @Override
> protected Object load() {
> return new LinkedList( leagueService.getRanking( leagueService.loadRound( (Long) master.getObject( null ) ) ) );
> }
> }
Nothing shocking. Just remember to annotate your class with
@Configurable and make your reference to LeagueService transient.
You need to put an additional file on the classpath to make it all work.
The location should be META-INF/aop.xml. Mine states:
> <aspectj>
> <weaver options="-showWeaveInfo -XmessageHandlerClass: org.springframework.aop.aspectj.AspectJWeaverMessageHandler">
> <include within="com.mobilebox.squasher..*"/>
> </weaver>
> <aspects>
> <include within=" org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"/>
> </aspects>
> </aspectj>
You can drop aspectj/weaver/@options. This is only for debugging
purposes. Remember to change aspectj/weaver/include/@within attribute to
match the package (and subpackages) you want weaved.
That's all for coding. Let's run it.
- o - Running - o -
Put aspectjrt.jar on classpath. Create you JVM with additional option
(adjust aspectjweaver location):
> java -javaagent:lib/aspectjweaver.jar com.mobilebox.squasher.launcher.JettyRunner
If you run your container from withing Eclipse IDE put
'-javaagent:lib/aspectjweaver.jar' in 'VM arguments' editbox.
That's all. AspectJ will weave your RankingModel class on load and
inject leagueService reference. In case of my project that was starting
in 5.5 seconds it added 2 more seconds. You can skip Load-Time Weaving
(LTW) if you just used aspectj compiler or weaved your classes offline.
- o - Serialization - o -
Exactly. Wicket is know with it's I-serialize-everything-like-crazy.
Turns out this is already handled. You need to make references to your
services *transient* so they are not serialized. Fortunately when
deserializing and object from stream AOP also kicks in so you have your
service reference reinjected. Pure fun!
- o - Gotchas - o -
Every programming solutions got one. This one also. With current
@Configurable implementation services get injected AFTER the injectee is
created (after all constructors got invoked). That means this won't work:
> public class LeaguePage extends BaseSquasherPage {
> private LeagueService leagueService;
>
> public LeaguePage( PageParameters parameters ) throws StringValueConversionException {
> Long leagueId = parameters.getLong( "id" );
>
> // nasty NPE here!
> League league = leagueService.loadLeague( leagueId );
>
> add( new Label( "leagueName", league.getName() ) );
> }
> }
You can do 2 things:
- implement another AOP pointcut so services get injected before
construction (do not know if that is possible, will dig further)
- be smarter than AOP itself:
> @Configurable(autowire = Autowire.BY_NAME, dependencyCheck = true)
> public class LeagueServiceDelegate implements Serializable, LeagueService {
> private transient LeagueService leagueService;
>
> public void setLeagueService( LeagueService leagueService ) {
> this.leagueService = leagueService;
> }
>
> public League loadLeague( Long id ) {
> return leagueService.loadLeague( id );
> }
> }
> public class LeaguePage extends BaseSquasherPage {
> private LeagueServiceDelegate leagueService = new LeagueServiceDelegate();
>
> public LeaguePage( PageParameters parameters ) throws StringValueConversionException {
> Long leagueId = parameters.getLong( "id" );
>
> // the delegate is already up and running
> League league = leagueService.loadLeague( leagueId );
>
> add( new Label( "leagueName", league.getName() ) );
> }
> }
With eclipse you can create such delegate in 30 seconds no matter how
big the target service is. (generate setter, generate delegates).
- o - Post scriptum - o -
If anyone is interested I will blogify this entry along with sample
working project to test. Just let me know.
[1]
http://www.springframework.org/docs/reference/aop.html#aop-atconfigurable
--
Leszek Gawron
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Wicket-user mailing list
Wicket-user@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/wicket-user
------------------------------------------------------------------------- Take Surveys. Earn Cash. Influence the Future of IT Join SourceForge.net's Techsay panel and you'll get the chance to share your opinions on IT & business topics through brief surveys -- and earn cash http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________ Wicket-user mailing list Wicket-user@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/wicket-user