The State of Wicket

Preview:

DESCRIPTION

The current state of the Apache Wicket framework in 2014 as presented at the DEVdev meetup held in Deventer, the Netherlands. - A critique of ThoughtWorks' Technology Review 2014 where they slam JSF (jay) as a concept (nay) - A look back at 10 years of Wicket - A review of the current Wicket versions - An outlook and roadmap for Wicket 7 and Wicket 8 The DEVdev (Deventer Developers) is a new meetup for any developer in the eastern part of the Netherlands (the right side of the IJssel river). This presentation was delivered at the first meetup, and was kindly sponsored by Topicus B.V.

Citation preview

State of Apache Wicket

DEVELOPERSENTERDEV

powered by Topicus

presented at:

topicus

sponsored by

“We continue to see teams run into trouble using JSF -- JavaServer Faces -- and are recommending you avoid this technology.”

–ThoughtWorks Technology Radar January 2014

Wicket Developers Rejoice! !Our archenemy has been denounced!!!

Or is there more to this story?

“We continue to see teams run into trouble using JSF -- JavaServer Faces -- and are recommending you avoid this technology. Teams seem to choose JSF because it is a J2EE standard without really evaluating whether the programming model suits them. We think JSF is flawed because it tries to abstract away HTML, CSS and HTTP, exactly the reverse of what modern web frameworks do. JSF, like ASP.NET webforms, attempts to create statefulness on top of the stateless protocol HTTP and ends up causing a whole host of problems involving shared server-side state. We are aware of the improvements in JSF 2.0, but think the model is fundamentally broken. We recommend teams use simple frameworks and embrace and understand web technologies including HTTP, HTML and CSS.”–ThoughtWorks Technology Radar January 2014

The full quote from ThoughtWorks

Technology Radar. The good bits are

on the next slide...

“We continue to see teams run into trouble using JSF and are recommending you avoid this technology. We think JSF is flawed because it tries to abstract away HTML, CSS and HTTP. JSF, like ASP.NET webforms, attempts to create statefulness on top of the stateless protocol HTTP. We think the model is fundamentally broken.”

–ThoughtWorks Technology Radar January 2014

This is not a critique of JSF in particular but (server side) component frameworks. The grunt of the critique is that JSF attempts to create state fullness on top of the stateless protocol–which is precisely what JSF, .Net, Wicket and Tapestry are doing.

“We recommend teams use simple frameworks and embrace and understand web technologies

including HTTP, HTML and CSS.”

I don’t agree with this assessment:!

server side, state managing frameworks were

created to provide a solution for problems

that are difficult to solve with “simple frameworks”. !

Going back to 2004 is not the solution!

–ThoughtWorks Technology Radar January 2014

A typical screen in one of our 1000+ page multi-tenant SaaS applications.!Would a“simple framework” make it possible to maintain 3 of these 1M lines of code applications with just 30 developers?

10 years of Apache Wicket

2004

2004

The Server Side

2004

The Server SideThe original Wicket website. We have seen several different stylings over the years...

20052004

codehaus.org

The Server Side

20052004

codehaus.org

The Server Side

The first Wicket meetup in 2005 in Deventer at the Topicus offices.

20052004

codehaus.org

The Server Side

20052004

codehaus.org

The Server Side

The web framework shoot-out at JavaOne 2005. Now that was fun!

JSF Tapestry

Struts 2 (originally Webwork)

Wicket Shale

codehaus.org

2004

The Server Side JavaOne

2005

codehaus.org

2004

The Server Side JavaOne

1.0

2005

codehaus.org

2004

The Server Side JavaOne

1.0

1.1

2005

codehaus.org

2004

The Server Side JavaOne

1.0

1.1

2006

1.2

2005

2007

2007

Wicket joins !The Apache Software Foundation!

2007

One of 3 Wicket meetups in

Amsterdam organised by Arjé

Cahn (Hippo CMS)

2007

2007

1.3

2008

2007

1.3

2008

2007

1.3

2008

2007

1.3

2008 2009

1.4

2007

1.3

2008 2009

1.4

2010

2007

1.3

2008 2009

1.4

2010 2011

1.5

2012 2013 2014

2012

6.0

2013 2014

2012

6.0

2013 2014

6.126.2

6.46.6

6.86.10

6.16.3

6.56.7

6.96.11

6.13

State of Apache Wicket

1. core

2. extensions

3. spring

4. datetime

5. auth-roles

Mailinglist traffic

Commit activity

Wicket in ActionWicket Cookbook

Commit activity

Wicket in ActionWicket Cookbook

book writing has caused quite a dent

in our Wicket related activities:!

burn-out plays a major part.

In a Nutshell, Wicket...

– Ohloh report for Wicket

In a Nutshell, Wicket...

… has had 17,645 commits made by 52 contributors representing 314,959 lines of code

– Ohloh report for Wicket

In a Nutshell, Wicket...

… has had 17,645 commits made by 52 contributors representing 314,959 lines of code

… is mostly written in Java witha well-commented source code

– Ohloh report for Wicket

In a Nutshell, Wicket...

… has had 17,645 commits made by 52 contributors representing 314,959 lines of code

… is mostly written in Java witha well-commented source code

… has a well established, mature codebase maintained by a large development team with stable Y-O-Y commits

– Ohloh report for Wicket

In a Nutshell, Wicket...

… has had 17,645 commits made by 52 contributors representing 314,959 lines of code

… is mostly written in Java witha well-commented source code

… has a well established, mature codebase maintained by a large development team with stable Y-O-Y commits

… took an estimated 83 years of effort (COCOMO model) starting with its first commit in September, 2004ending with its most recent commit 2 days ago– Ohloh report for Wicket

Wicket 1.4

security fixes only

Wicket 1.5

security fixes only

Wicket 6continues to be released along

side Wicket 7, monthlies will

transform into bi-monthlies when

number of resolved tickets subsides.

Java 6

semantic versioning

Works wonderful for us. Only snag: JQuery updates.

Monthly releases

also wonderful: no longer waiting for a long time for a fixed bug.

Wicket 7

not much to say about Wicket 7: no major API changes.

Java 7

Servlet 3

Minor API breaks

Wicket 8Anything written here is just

speculation from my side. This is not set in stone, this is not how

we are going to implement things, or quite reasonably at all.

Java 8

PROJECT LAMBDA

“functional” programming in Java

@FunctionalInterface public interface ILinkListener { void onLickClicked(); }

ILinkListener l = new ILinkListener() { @Override public void onLinkClicked() { System.out.println("Klik"); } }

ILinkListener l = () -> { System.out.println("Klik"); }

ILinkListener l = new ILinkListener() { @Override public void onLinkClicked() { System.out.println("Klik"); } }

-of- !ILinkListener l = () -> { System.out.println("Klik"); }

add(new Link<Void>("save") { @Override public void onClick() { dao.save(object); getSession().info("Saved."); setResponsePage(new OtherPage()) } });

add(new Link<Void>("save") { @Override public void onClick() { dao.save(object); getSession().info("Saved."); setResponsePage(new OtherPage()) } });

add(new Link<>("save").onClick(()-> { dao.save(object); getSession().info("Saved."); setResponsePage(new OtherPage()); });

private void onSave() { dao.save(object); getSession().info("Saved."); setResponsePage(new OtherPage()) } !!!!!!

private void onSave() { dao.save(object); getSession().info("Saved."); setResponsePage(new OtherPage()) } !!add(new Link<Void>("save") .onClick(this::onSave); !!

A link with onclick, visibility and body

add(new Link<Void>("like") { @Override public void onClick() { person.likedBy(me); } @Override public boolean isVisible() { return person.isNotLiked(); } }.setBody(new AbstractReadOnlyModel<String>() { @Override public String getObject() { StringBuilder sb = new StringBuilder("Like "); sb.append(person.getFirstName()); return sb.toString(); } }));

add(new Link<Void>("like") { @Override public void onClick() { person.likedBy(me); } @Override public boolean isVisible() { return person.isNotLiked(); } }.setBody(new AbstractReadOnlyModel<String>() { @Override public String getObject() { StringBuilder sb = new StringBuilder("Like "); sb.append(person.getFirstName()); return sb.toString(); } }));

add(new Link<Void>("like") { @Override public void onClick() { person.likedBy(me); } @Override public boolean isVisible() { return person.isNotLiked(); } }.setBody(new AbstractReadOnlyModel<String>() { @Override public String getObject() { StringBuilder sb = new StringBuilder("Like "); sb.append(person.getFirstName()); return sb.toString(); } }));

add(new Link<Void>("like") { @Override public void onClick() { person.likedBy(me); } @Override public boolean isVisible() { return person.isNotLiked(); } }.setBody(new AbstractReadOnlyModel<String>() { @Override public String getObject() { StringBuilder sb = new StringBuilder("Like "); sb.append(person.getFirstName()); return sb.toString(); } }));

add(new Link<Void>("like") { @Override public void onClick() { person.likedBy(me); } @Override public boolean isVisible() { return person.isNotLiked(); } }.setBody(new AbstractReadOnlyModel<String>() { @Override public String getObject() { StringBuilder sb = new StringBuilder("Like "); sb.append(person.getFirstName()); return sb.toString(); } }));

add(new Link<Void>("like") { @Override public void onClick() { person.likedBy(me); } @Override public boolean isVisible() { return person.isNotLiked(); } }.setBody(new AbstractReadOnlyModel<String>() { @Override public String getObject() { StringBuilder sb = new StringBuilder("Like "); sb.append(person.getFirstName()); return sb.toString(); } }));

add(new Link<Void>("like") { @Override public void onClick() { person.likedBy(me); } @Override public boolean isVisible() { return person.isNotLiked(); } }.setBody(new AbstractReadOnlyModel<String>() { @Override public String getObject() { StringBuilder sb = new StringBuilder("Like "); sb.append(person.getFirstName()); return sb.toString(); } }));

A link with onclick, visibility and body

add(new Link<>("like") .visible(() -> person.isNotLiked()) .onClick(() -> person.likedBy(me)) .body(() -> { StringBuilder sb = new StringBuilder("L sb.append(person.getFirstName()); return sb.toString(); }) );

add(new Link<>("like") .visible(() -> person.isNotLiked()) .onClick(() -> person.likedBy(me)) .body(() -> { StringBuilder sb = new StringBuilder("L sb.append(person.getFirstName()); return sb.toString(); }) );

add(new Link<>("like") .visible(() -> person.isNotLiked()) .onClick(() -> person.likedBy(me)) .body(() -> { StringBuilder sb = new StringBuilder("L sb.append(person.getFirstName()); return sb.toString(); }) );

add(new Link<>("like") .visible(() -> person.isNotLiked()) .onClick(() -> person.likedBy(me)) .body(() -> { StringBuilder sb = new StringBuilder("L sb.append(person.getFirstName()); return sb.toString(); }) );

add(new Link<>("like") .visible(() -> person.isNotLiked()) .onClick(() -> person.likedBy(me)) .body(() -> { StringBuilder sb = new StringBuilder("L sb.append(person.getFirstName()); return sb.toString(); }) );

add(new Link<>("like") .visible(() -> person.isNotLiked()) .onClick(() -> person.likedBy(me)) .body(() -> { StringBuilder sb = new StringBuilder("L sb.append(person.getFirstName()); return sb.toString(); }) );

Anon inner classes: 17 lines Java 8 lambdas: 9 lines

add(new Link<Void>("like") { @Override public void onClick() { person.likedBy(me); } @Override public boolean isVisible() { return person.isNotLiked(); } }.setBody(new AbstractReadOnlyModel<String>() { @Override public String getObject() { StringBuilder sb = new StringBuilder("Like "); sb.append(person.getFirstName()); return sb.toString(); } }));

add(new Link<>("like") .visible(() -> person.isNotLiked()) .onClick(() -> person.likedBy(me)) .body(() -> { StringBuilder sb = new StringBuilder("L sb.append(person.getFirstName()); return sb.toString(); }) ); !!!!!!!!

nashorn

JavaScript validation

ScriptEngineManager m = new ScriptEngineManager(); !ScriptEngine nashorn = m.getEngineByName("nashorn"); !nashorn.put("age", validatable.getValue()); String js = "age >= 18"; !try { Object result = nashorn.eval(js); if(!((Boolean)result) { ValidationError e = new ValidationError(); validatable.error(e); } } catch(Exception e) { }

ScriptEngineManager m = new ScriptEngineManager(); !ScriptEngine nashorn = m.getEngineByName("nashorn"); !nashorn.put("age", validatable.getValue()); String js = "age >= 18"; !try { Object result = nashorn.eval(js); if(!((Boolean)result) { ValidationError e = new ValidationError(); validatable.error(e); } } catch(Exception e) { }

ScriptEngineManager m = new ScriptEngineManager(); !ScriptEngine nashorn = m.getEngineByName("nashorn"); !nashorn.put("age", validatable.getValue()); String js = "age >= 18"; !try { Object result = nashorn.eval(js); if(!((Boolean)result) { ValidationError e = new ValidationError(); validatable.error(e); } } catch(Exception e) { }

ScriptEngineManager m = new ScriptEngineManager(); !ScriptEngine nashorn = m.getEngineByName("nashorn"); !nashorn.put("age", validatable.getValue()); String js = "age >= 18"; !try { Object result = nashorn.eval(js); if(!((Boolean)result) { ValidationError e = new ValidationError(); validatable.error(e); } } catch(Exception e) { }

ScriptEngineManager m = new ScriptEngineManager(); !ScriptEngine nashorn = m.getEngineByName("nashorn"); !nashorn.put("age", validatable.getValue()); String js = "age >= 18"; !Object result = nashorn.eval(js); try { if(!((Boolean)result) { ValidationError e = new ValidationError(); validatable.error(e); } } catch(Exception e) { }

ScriptEngineManager m = new ScriptEngineManager(); !ScriptEngine nashorn = m.getEngineByName("nashorn"); !nashorn.put("age", validatable.getValue()); String js = "age >= 18"; !Object result = nashorn.eval(js); if(!((Boolean)result) { ValidationError e = new ValidationError(); validatable.error(e); } try { } catch(Exception e) { }

ScriptEngineManager m = new ScriptEngineManager(); !ScriptEngine nashorn = m.getEngineByName("nashorn"); !nashorn.put("age", validatable.getValue()); String js = "age >= 18"; !try { Object result = nashorn.eval(js); if(!((Boolean)result) { ValidationError e = new ValidationError(); validatable.error(e); } } catch (Exception e) { }

ScriptEngineManager m = !ScriptEngine nashorn = m.getEngineByName(!nashorn.put(String js = !try Object result = nashorn.eval(js); ValidationError e = new ValidationError() validatable.error(e); } } }

This was only a proof-of-concept. This probably won’t ever fly due to business entities residing on

server, and difficult to share those in browser, including I18N

messages.

java.timesupport for this will be for

converters, validations and possibly wicket-datetime

RoadmapAnything written here is just

speculation from my side. This is not set in stone. Dates are mere

guidelines.

Monthly releases

6.14 6.15

7.0-M17.0-M2

7.0

6.18

feb mar may

2014

7.1

6.19

jun

7.9 7.10

8.0-M18.0-M2

8.0

7.12

feb mar may

2015

8.1

7.13

jun

git organization

master wicket-1.0.x wicket-1.1.x wicket-1.2.x wicket-1.3.x wicket-1.4.x wicket-1.5.x wicket-6.x !

Current organisation of our repository: master is new development, rest

is maintenance.

master wicket-1.0.x wicket-1.1.x wicket-1.2.x wicket-1.3.x wicket-1.4.x wicket-1.5.x wicket-6.x !

wicket 7.x!

Current organisation of our repository: master is new development, rest

is maintenance.

master wicket-1.0.x wicket-1.1.x wicket-1.2.x wicket-1.3.x wicket-1.4.x wicket-1.5.x wicket-6.x wicket-7.x wicket-8.x

proposed layout: no more master, but just product branches.

experimental modulesJay! Many experimental modules have been upgraded to core modules! beanvalidation, CDI-1.1, web sockets will be part of Wicket core from 6.14 and onwards.

org.apache.wicket.experimental.wicket-6.x wicket-atmosphere wicket-bootstrap wicket-new-examples !org.apache.wicket.experimental.wicket-7.x wicket-atmosphere wicket-cdi-1.1

Remaining experimental modules

org.apache.wicket.experimental.wicket-6.x wicket-atmosphere wicket-bootstrap wicket-new-examples !org.apache.wicket.experimental.wicket-7.x wicket-atmosphere wicket-cdi-1.1

No more bootstrap: bootstrap is

just a resource reference to

JavaScript and CSS. Write your

own (~10 lines of code) or use

L0rdn1kk0n’s bootstrap wicket component library.

new examples: no time to do anything useful with them.

org.apache.wicket.experimental.wicket-6.x wicket-atmosphere !!org.apache.wicket.experimental.wicket-7.x wicket-atmosphere !

Wic

ket 6

.xW

icke

t 7.x

How to separate the same experimental modules in multiple

product lines?

org.apache.wicket.experimental.wicket-6.x wicket-atmosphere !!org.apache.wicket.experimental.wicket-7.x wicket-atmosphere !

Wic

ket 6

.xW

icke

t 7.x

Make the groupId specific to the product line, keep code as similar as possible.

The state of Apache Wicket:

The state of Apache Wicket:

HEALTHY

DEVELOPERS

ENTERDEVpowered by Topicus

Recommended