Title: Called on 'hello' event. public void handleEventHello ...
1Step-by-Step Legacy Migration with Aranea
- Jevgeni Kabanov
- RD lead, Aranea project lead
- Webmedia, Ltd.
- ekabanov_at_webmedia.ee
2Motivating scenario
- Stakeholders have a large web application
written in Struts. They consider Struts legacy
and want to continue development in JSF. However
rewriting all of the code would take too much
time, effort and money and would halt the ongoing
development.
3Our solution
- Use Aranea web integration layer to run different
technologies side-by-side - Refactor the application into independent
coarse-grained components - Start new development immediately and change old
code only when requirements change step-by-step
migration
4(No Transcript)
5(No Transcript)
6(No Transcript)
7(No Transcript)
8Goal
- Get rid of legacy and custom web frameworks in
your application
9Aranea
- Aranea began as an Object-Oriented MVC Web
Framework - From the onset we had plans to build web
integration on the same platform - Aranea Integration has been released to public
yesterday )
10Disclaimer
- Aranea MVC is stable and used in production
- Aranea Integration is beta and used in pilot
migration projects - Everything is Open-Source with documentation and
support available for free from
araneaframework.org - Commercial support/training/consulting is
provided at araneaframework.com
11Organization
- Aranea Component Model
- Widgets
- Flow navigation
- Aranea Integration Layer
- Struts, JSF, GWT
- Step-by-step migration
- Principles
- Case study
12Aranea Component Model
- Every component is a first-class object
- Objects are created by the programmer
- No (XML) mappings
- State is in the object (no scopes)
- Components, pages and flows are represented by
first-class widgets
13Hello World!
NameWidget name.jsp Reads the name from requests
and passes it to HelloWidget
HelloWidget hello.jsp Renders the Hello
name! greeting, where name is given by the
caller.
14NameWidget
public class NameWidget extends BaseUIWidget
//Called on hello event public void
handleEventHello() String name //reads
name from request parameters (String)
getScopedData().get("name")
getFlowCtx().replace(new HelloWidget(name))
name.jsp
Insert your name ltinput typetext
namewidgetId.name"/gtltbr/gtltbr/gt
ltuieventButton labelId"Say hello"
eventId"hello"/gt
15HelloWidget
public class HelloWidget extends BaseUIWidget
private String name //Widget state is in its
fields public HelloWidget(String name)
this.name name //We could pass any Java object
here public String getName() return
this.name public void handleEventBack()
getFlowCtx().replace(new NameWidget())
hello.jsp
Hello widget.name! ltbr/gt ltuieventButton
labelId"Back" eventId"back"/gt
16web.xml
... ltservletgt ltservlet-namegtaraneaServletlt/ser
vlet-namegt ltservlet-classgtAraneaSpringDispatch
erServletlt/servlet-classgt ltinit-paramgt
ltparam-namegtaraneaApplicationStartlt/param-namegt
ltparam-valuegtexample.NameWidgetlt/param-valuegt
lt/init-paramgt ltload-on-startupgt1lt/load-on-
startupgt lt/servletgt ltservlet-mappinggt
ltservlet-namegtaraneaServletlt/servlet-namegt
lturl-patterngt/main/lt/url-patterngt lt/servlet-mappi
nggt ...
17Flows
- Currently we use replace() which means
- A new instance is created every time
- We know where to return
Flow1
18Flows
- What we would want is to preserve the instance
and nest the new flow
Flow1
Flow2
19Flows
- start() and finish() do exactly that
public class NameWidget extends BaseUIWidget
... public void handleEventHello() ...
getFlowCtx().start(new HelloWidget(name))
public class HelloWidget extends BaseUIWidget
... public void handleEventBack()
getFlowCtx().finish(null)
20Including widgets
- Widgets can be included, lets try to use
HelloWidget inside NameWidget like this
HelloWidget
handleEventHello()
We assume that the back button was removed from
HelloWidget
21Including widgets
- First lets modify the HelloWidget
public class HelloWidget extends BaseUIWidget
private String name public HelloWidget(String
name) this.name name public String
getName() return this.name public
void setName(String name) this.name
name
22Including widgets
- Now lets add a HelloWidget instance
public class NameWidget extends BaseUIWidget
private HelloWidget helloWidget protected
void init() helloWidget new
HelloWidget("Stranger") addWidget("hello",
helloWidget) public void
handleEventHello() String name (String)
getScopedData().get("name")
helloWidget.setName(name)
23Including widgets
- And finally we include the widget in the JSP
ltuiwidgetInclude id"hello"/gtltbr/gt Insert your
name ltinput type"text namewidgetId.name"/
gtltbr/gtltbr/gt ltuieventButton labelId"Say
hello" eventId"hello"/gt
24Including widgets
HelloWidget, helloWidget ltuiwidgetInclude
idhello/gt
helloWidget.setName(Jevgeni)
25Widgets are objects
- We can include several widgets of same class on
one page
public class RootWidget extends BaseUIWidget
protected void init() addWidget("hello1",
new NameWidget()) addWidget("hello2", new
NameWidget()) addWidget("hello3", new
NameWidget())
26Flows are objects
- We can also include several flow containers on
one page
public class RootWidget extends BaseUIWidget
protected void init()
addWidget("flowContainer1", new
StandardFlowContainerWidget(new NameWidget()))
addWidget("flowContainer2", new
StandardFlowContainerWidget(new NameWidget()))
addWidget("flowContainer3", new
StandardFlowContainerWidget(new NameWidget()))
27Goal
- Get rid of legacy and custom web frameworks in
your application
28Our Solution
- Use Aranea web integration layer to run different
technologies side-by-side - Refactor the application into coarse-grained
integration components - Start new development immediately and change old
code only when requirements change step-by-step
migration
29Requirements
- We want to implement widgets using any
framework/technology available - This can mean running a whole application in one
widget and another application in its sibling - Without any changes to the technology
- In fact we want to do that retroactively, reusing
existing applications
30Aranea Integration Layer
- Integration Layer API is based around widgets
- StrutsWidget, JsfWidget, GwtWidget
- Widgets receive the URI of the starting point of
the subapplication - E.g. new StrutsWidget(/Welcome.do)
- AraneaUtil gives access to all of the Aranea API
from embedded applications
31Struts Integration Problems
- Session and request attributes share the same
namespace and can clash - Request parameter names will clash already during
form submission - Struts navigates between pages by changing the
actual URL - HTML limits usage of some tags
32Problem 1 Attributes
- We can make request attributes local, by wrapping
HttpServletRequest and saving them in a local map - Since HttpSession can only be accessed via
HttpServletRequest we can do the same thing to
session attributes
33Problem 2 Parameters
- Since parameter names clash already during the
submission of request we need to solve the
problem in HTML - We can do it by introducing prefixes to each
field name referring to the containing widget - The request wrapper restores the original names
34Problem 3 Navigation
- We put a filter over the Struts servlet that will
include the Aranea servlet - While Aranea renders the particular StrutsWidget,
it will include the according action - Therefore it will render in correct place as will
everything else
35Problem 3 Navigation
- However we need to include some information that
is used to render Aranea - Servlet path
- Window id
- Current widget id
- We do that by overriding encodeURL() in request
wrapper
36Problem 4 HTML
- There are two main things we need to change in
Struts HTML output - Forms cannot be nested and must be escaped
- Field names must be prefixed
- These are easy to change using a lexer (not even
a parser) on the output stream - To escape forms we construct a JavaScript object
with the same properties/methods
37name.jsp hello.jsp
lthtmlgt ltbodygt ltform method"get"
action"ltresponse.encodeURL("hello.jsp")gt"gt
ltinput name"name" type"text"/gt ltinput
type"submit" value"Say hello!"gt
lt/formgt lt/bodygt lt/htmlgt
lthtmlgt ltbodygt Hello param.name! lta
href"ltresponse.encodeURL("name.jsp")gt"gtBacklt/a
gt lt/bodygt lt/htmlgt
38HelloNameWidget RootWidget
public class HelloNameWidget extends StrutsWidget
public HelloNameWidget()
super("/name.jsp")
public class RootWidget extends BaseUIWidget
protected void init() addWidget("hello1",
new HelloNameWidget()) setViewSelector("root"
)
Output
ltinput name"f0.hello1.name" type"text"/gt
ltinput type"submit" onclicknew
Aranea.Struts.Form().submit()"
value"Say hello!"gt
39What will happen?
public class RootWidget extends BaseUIWidget
protected void init() addWidget("hello1",
new HelloNameWidget()) addWidget("hello2",
new HelloNameWidget()) addWidget("hello3",
new HelloNameWidget())
setViewSelector("root")
ltuiwidgetInclude id"hello1"/gtltbr/gtltbr/gt ltuiwidg
etInclude id"hello2"/gtltbr/gtltbr/gt ltuiwidgetInclud
e id"hello3"/gt
40DEMO
41Generalizing Integration
- The approach taken with Struts can be easily
extended to any action-based framework - Including custom ones
- In fact most of it is applicable to
component-based frameworks as well - However component-based frameworks will usually
allow us to solve these problems simpler
42JSF Integration
- We use the same approach to encapsulate request
and session attributes - Form fields can be prefixed by overriding the
naming container (form) - Navigation can be solved by overridding the view
handler - No postprocessing necessary!
43DEMO
44GWT Integration
- Essentially the simplest, as almost everything
happens on the client side - Two problems
- Using RPC to call widget methods
- Restoring client-side state after a full request
- Not solved yet!
45Our Vision
46Goal
- Get rid of legacy and custom web frameworks in
your application
47Our Solution
- Use Aranea web integration layer to run different
technologies side-by-side - Refactor the application into coarse-grained
integration components - Start new development immediately and change old
code only when requirements change step-by-step
migration
48Refactoring
- Enable Aranea Integration and sanitize HTML
(produces working application) - Extract layout, menu and login
- Split application into coarse-grained components
49Case Study
- Estonian Tax Inspection application module
- Connected with Forestry and European Union
directives - Part of a large application family based on
common architecture that manage all tax and
customs needs
50Technologies
- MVC web framework is Struts
- Presentation done using Velocity and Tiles
- A lot of custom extensions to all of them
- SSO using Weblogic API
51Step 1 Integration HTML
- Application started running after initial
configuration - HTML ltagt tags were used in some places, which
means encodeURL() was not applied - Some scripts accessed form fields by name
- Added a widget prefix before the name
52Step 2 Layout, menu and login
- Since login was done using SSO we left it be
- We extended the Aranea MenuWidget to implement
the menu from scratch, reusing all the old
privileges - After lifting header, footer and menu to the
RootWidget turned out that Tiles were not doing
anything useful anymore and could be eliminated
53Step 3 How to split?
- First we extract the widgets pointing to the
relevant parts of the application - Next we change all navigation/inclusion between
those parts to use Aranea API - Sending events to the hosting widget
- AraneaUtil.getFlowCtx(), AraneaUtil.addWidget(),
ltuihostedWidgetIncludegt used from the embedded
applications
54Step 3 Analyze and split
- After some research we decided that the best way
to split the application would be vertically by
functionality - We ended up with five different functionality
types and about ten different widgets (some
widgets were multipurpose)
55Step 3 Analyze and split
- Some widgets were used only as flows while others
were included as components - By extracting and abstracting several included
widgets we eliminated a lot of copy-paste - While we could further refine our components it
was good enough for starting migration
56Results
- Five main functionality parts which could be
rewritten one by one without affecting the others - No more Tiles
- Less copy-paste
- If we needed to add a sixth functionality part we
could start using JSF immediately
57Goal
- Get rid of legacy and custom web frameworks in
your application
58Our solution
- Use Aranea web integration layer to run different
technologies side-by-side - Refactor the application into coarse-grained
integration components - Start new development immediately and change old
code only when requirements change step-by-step
migration
59Step-by-step migration
- After refactoring every component is a Java class
with a particular interface/contract - The rest of the components can only interact with
it via that contract - Therefore we can just rewrite it using any other
implementation as long as the contract is
preserved
60Case study migration
- In the case study we wanted to use Aranea, so
there was no need for further migration - Eventually we would like to lift the whole Tax
and Customs application to Aranea using other
framework features when needed - However we also have clients who prefer JSF and
Tapestry, so in those cases we would continue
61The Next Step
- Aranea Integration solves the problem of mashing
up Java web applications - A lot of legacy applications are written in Perl,
PHP, Oracle Forms, etc - Youd also want to integrate with .NET web
applications - Aranea Remote Integration will support that!
62Webmedia
- Webmedia (www.webmedia.eu) is a Baltic company
employing over 300 people that sponsors Aranea
development - Webmedia offers complete commercial support for
Aranea MVC Integration - In fact we now also offer support for migrating
your legacy web applications to a platform of
your choice )
63Final Words
- Using Aranea Integration is easy
- Problems might come up requiring better
understanding of Integration works - Migration is not completely painless, but it is
cheap next to the alternative - Migrated Struts Mailreader application in the
distribution is a good starting point
64Questions
www.araneaframework.org
www.araneaframework.com