Idiomatic RSF - BeanGuards and OTP

bricklayerbelchedInternet and Web Development

Feb 5, 2013 (4 years and 4 months ago)

103 views

Creative Commons Attribution
-
NonCommercial
-
ShareAlike 2.5 License

Sakai Programmer's Café

Saki P
UK

Idiomatic RSF



OTP, Messaging and
BeanGuards

Antranig Basman,

CARET, University of Cambridge

Recap of RSF Design


Three main threads


On the design side, completely pure (X)HTML
templates, previewability of appearance AND
behaviour (IKAT renderer)


On the code side, a completely unintrusive
binding to an application model (EL)


Portability of applications assured through request
-
scope IoC (RSAC)


based on the Spring framework


Statelessness and transparency of application
behaviour through request cycle and ViewParameters


In between, a completely technology
-
neutral
and behaviour
-
free representation of the
*function* of a user interface (component tree)

Simple Rendering Sample

Hello,
<
span
rsf:id
=
"current
-
user
-
name"
>
Current User
</
span
>


Uses an rsf:id on an HTML entity to show
where to place the dynamic text


Does not have to be a span or div only!


UIOutput will send the escaped string to the id
location in the component tree


UIBranchContainer and colon tags allow
complex looping and branching without
polluting the template


UIOutput.
make
(tofill,
"current
-
user
-
name"
,


logic
.getCurrentUserDisplayName());

EL in RSF


VERY lightweight


Just a dot
-
separated path of bean names and properties
(or 0
-
arg method for a method binding)


Binds component tree to the model without polluting it


No logic allowed


Example from CRUD tool


an input field from
AddItemProducer.java:



UIInput
.
make
(
addupdateitem
,

"item
-
title"
,



"#{itemsBean.newItem.title}"
,

DEFAULT_TITLE

);



Component


class

EL
Reference

Initial
value

Quick recap of Spring


A Spring bean has no framework dependencies (in
Spring, as in RSF)


Is configured in an XML file, which specifies
bean class, bean properties and other
dependencies (other beans)


Here is the itemsBean we referred to earlier:

<
bean
id
=
"itemsBean"


class
=
"org.sakaiproject.crudplus.tool.ItemsBean"


init
-
method
=
"init"
>


<
property
name
=
"logic"


ref
=
"org.sakaiproject.crudplus.logic.CrudPlusLogic"
/>


<
property
name
=
"messages"
ref
=
“targettedMessageList"
/>

</
bean
>

Bean
name

Injected

Dependency

ItemsBean definition

public

class

ItemsBean
{


public

CrudPlusItem
newItem

=

new

CrudPlusItem
();


public

Map
selectedIds

=

new

HashMap
();



private

CrudPlusLogic
logic
;


public

void

setLogic(
CrudPlusLogic logic
)

{


this
.
logic

=

logic
;


}

...








Initial section of ItemsBean


Recall that our EL was
#{itemsBean.newItem.title}


Classic example of the “captive entity pattern”


Halfway towards to
OTP
(which we will talk about NOW!)

Reworked CrudPlusItem sample


Discuss the RSF inter
-
view messaging system
based on TargettedMessageList


Streamline code with more advanced RSF features


Validation is packaged as reusable POJOs through
“BeanGuards”


Also reworked to OTP (One True Path) ORM
idiom


This idiom is less appropriate here than it would be in a
completely “flat” model but still adds some value

I18N in RSF


Since 0.7.1, done primarily through the
UIMessage component


Use UIMessage directly rather than UIOutput
to render a localised message



Use UIMessage as a nested component in
UICommand, UILink and Decorators to render
a localised label




Use setMessageKeys() on a UISelect
component to interpret label String array as
message keys

UIMessage
.make(
tofill
,

"level
-
header"
,

"modifytemplate.level.header"
,


new

String
[]

{
currentLevel
,

new

Integer
(
templateItemsList
.
size
()).
toString
(),

});

UIInternalLink
.make(
tofill
,

"modify_title_desc_link"
,

UIMessage
.make(
"modifytemplate.modify.title.desc.link"
),

new

TemplateViewParameters
(
ModifyTemplateProducer
.
VIEW_ID
,

templateId
));

Underlying I18N realities


Messages are formatted as if by JDK’s standard
MessageFormat (see Sun API)




Next layer is a Spring standard MessageSource
(by default
ReloadableResourceBundleMessageSource)


Top layer is RSF’s MessageLocator (application
scope bean named messageLocator) which
factorises off the dependence on the Locale and
adds more convenience signatures


Finally there is the UIMessage component which
allows access to the message bundle without
injecting anything

String result = MessageFormat.format( "At {1,time} on {1,date}, there was
{2} on planet {0,number,integer}.", arguments);

TargettedMessageList


Functionality will not be fully rounded out until 0.8, but
new in 0.7 is support for “info messages”


The “errors” scope is a kind of mini
-
flow scope, and is
targetted ONLY at the following view




targettedMessageList is a request
-
scope (unreasonable) bean


Inject it where you want (also has a standard proxy)


Localisation, rendering, and formatting comes for free


Messages appear in the template at the tag marked
rsf:id=“message
-
for:*”


Messages “widget” can be reskinned through
messageTemplateContributor

messages
.
addMessage
(
new

TargettedMessage
(
"item_updated"
,


new

Object
[] {
item
.
getTitle
()},

TargettedMessage
.
SEVERITY_INFO
));

More on
scr=
I


SCRs are provided for HTML <head>
aggregation in reusable components


scr=contribute
-
script

and
scr=contribute
-
style

mark CSS and JS declarations that need to be
aggregated from components


scr=head
-
collect

marks the place they should be
collected (automatically inferred)


scr=portal
-
matter

marks some <head> material
that is a placeholder for portal definitions and
should be replaced (in progress)

More on
scr=
II


Automatic inference of SCR tags is done by
a TemplateParseInterceptor






Any manual
scr=

declared in the template
will block automatic inference



public

interface

TemplateParseInterceptor
{


public

void

adjustAttributes


(
String tag
,

Map attributes
);


}

BeanGuards


for Validation


A declarative strategy for attaching rules to
modifications of the bean container


Similar to, for example, Spring’s declarative
transaction demarcation, or the Spring Acegi
security framework


Could also be seen as a kind of AOP


Although in this case the kind of AOP you can eat
between meals without losing your appetite


In any case, the key functionality is to provide
guaranteed execution of logic without intruding
either on the target, or on the validation code with
framework code

BeanGuards


Two main styles of BeanGuards


The Spring Validator interface is supported directly






Also supports pure POJO validators (preferred if Spring
portability is no concern)


Both of these are simply declared as beans
(typically at application scope)


*

@author

Rod

Johnson


public

interface

Validator
{


boolean

supports(
Class clazz
);


void

validate(
Object obj
,

Errors errors
);


}

Declaring a BeanGuard


As well as declaring the Guard itself, you must declare
WHAT

it is meant to be guarding


A Guard is targetted at a particular EL path within the
container


Many options for the “style” of the Guard, but typically
one guards
write

access to the target path


RSF contexts provide the helpful parent definition
writeGuardParent


For Spring Validator, you can use an inner bean for an
all
-
in
-
one definition:


<bean id="priceIncreaseGuard" parent="writeGuardParent">


<property name="guardedPath" value="priceIncrease"/>


<property name="guard">


<bean class="uk.org.ponder.rsf.springmvcstep.bus.PriceIncreaseValidator"/>


</property>


</bean>

Using a POJO Validator


For a POJO validator, the validator must be declared as a
request scope bean


Need to specify the property on the validator to receive the
target object


can be combined with validator bean name
to form an EL
-
like expression


<bean parent="writeGuardParent">


<property name="guardedPath" value="CrudPlusItem.*" />


<property name="guardProperty“ value="CrudPlusItemValidator.item" />


</bean>

<bean id="CrudPlusItemValidator"

class="org.sakaiproject.crudplus.tool.CrudPlusItemValidator" />

At request scope:

In code:

public

class

CrudPlusItemValidator
{


public

void

setItem(
CrudPlusItem newItem
)

{


if

(
newItem
.
getTitle
()

==

null

||

newItem
.
getTitle
().
equals
(
""
))

{


throw

new

IllegalArgumentException
(
"title_required"
);


}


}

}

POJO Validators vs. Spring Validators


Spring Validators


Are a somewhat recognised standard


Can be implemented in application scope


Cannot validate something which is null!


POJO Validators


Can use the “sleaze” style of throwing a
message key as exception text, and in that case
be completely dependency free


Can be written without casts


Would need to inject TargettedMessageList if
you wanted more complex message formatting

One True Path


RSF ORM “idiom” rather than “library”


OTP (= “One True Path”) assigns unique
EL path to each entity of data model


RSF “EL” is slimmed down so much
(only elementary reads/writes permitted)
that it is safe to address arbitrarily nasty
“POJO” models, even those managed by
Hibernate


In some cases can lead to the abolition of
“DAO”s


Is an idea of more general power (come
to “Helpers BOF”)

How does RSF OTP work?


Assigns a unique EL path to each Entity managed by ORM, where
read/write of the EL is
*directly*

bound to read/write of the model


The EL path for the CrudPlusItem with ID of 5 is
#{CrudPlusItem.5}


Add new entities with the special id form


#{CrudPlusItem.new 1}

Following two points are part of “Full” OTP but are not used in the OTP version of
CrudPlusTool


Issue special binding type (UIDeletionBinding) to “unlink” or
“delete” an existing entity


If the request concludes normally, commit


if any kind of exception
propagates out, rollback.



Not necessarily tied to any particular flavour or even use of ORM


EL paths are completely “generic”

How to set up OTP


Again, a general concept rather than a specific
library/implementation


Using RSFHibernate exposes the entire entity set
automatically via OTP


SakaiRSF contains
GenericDAOEntityBeanManager

which will automatically convert entities managed by
Aaron Zeckoski’s Generic DAO to OTP


In general PonderUtilCore contains
BasicObstinateEBL

(implementing
ObstinateEntityBeanLocator
) which will do most of
the legwork in setting up an OTP mapping


You can just do it yourself! (Can use plain Maps rather
than BeanLocators)

How to use OTP


In “pure” OTP, you can just emit bindings from the
producer, and do without any kind of backing bean at all
(see this style in OTP version of Vancouver’s TaskList app)







In CrudPlusItem, only makes sense to operate OTP for
“add/update” operations


UIInput
.make(
newtask
,

"new
-
task
-
name"
,

"#{Task.new 1.task}"
);


UICommand
.make(
newtask
,

"submit
-
new
-
task"
,

null
);


newtask
.
parameters
.
add
(


new

UIELBinding
(
"#{Task.new 1.owner}"
,

userId
));


newtask
.
parameters
.
add
(


new

UIELBinding
(
"#{Task.new 1.siteId}"
,

siteId
));

...


deleteform
.
parameters
.
add
(
new

UIDeletionBinding
(
"#{Task}"
,



new

ELReference
(
"#{deleteIds}"
)));

if

(
logic
.
canWriteItem
(
item
))

{

// check for current user and site


UIBoundBoolean
.make(
itemrow
,

"select
-
item"
,



"itemsBean.selectedIds."

+

item
.
getId
(),

Boolean
.
FALSE
);

When to use OTP


OTP comes into its own more and more in apps with
larger schemas, with more anaemic models


Is also much less trouble in single
-
ClassLoader
environments


Is particularly powerful for auto
-
generated apps (XML
ViewProducers, hbm2java models)


Since CRUDPlus has *some* logic (largely permissions
checking) in its logic layer, requires more work


Need to set up a “Front DAO”


CrudPlusLogicDAO


I still think the cost is just about worth it in this case


“Changed value logic” is much clearer


With bigger entities would save more and more time

The Magic Ingredient


What is this method in GenericDAO for?




“Natural” OTP semantics are Spring transactional semantics


Request concludes normally == commit transaction


Exception is thrown == perform rollback


RunnableInvoker is the greatest interface that was never in
the JDK


In other languages (Javascript, Ruby, any of the functional family
is not even necessary)

public

void

invokeTransactionalAccess(
Runnable toinvoke
)

{


toinvoke
.
run
();


}

The Great OSIV vs. DTO debate


OSIV = Open Session In View


DTO = Data Transfer Object


Both are answers to the architectural risk posed by Hibernate


OSIV costs are of leakage


Many believe that the view layer should be susceptible to raw
persistence exceptions


In fact RSF has a coherent exception
-
handling strategy and this
is not such a serious problem


DTO costs are of redundancy


Is an absolute, rather than a relative architectural cost (i.e. is
unconditionally bad! :) )


This debate is somewhat aligned to the Anaemic vs. Rich Domain
model debate (backwards


a DTO is a fabricated Anaemic model)


ONE purpose of
invokeTransactionAccess

is to enable OSIV

Why have I never seen this before?


Is the natural completion of Sakai’s “poor man’s Enterprise Container”
idiom


Currently obscured by the fact that Sakai users are “happy” to have
Hibernate as a global ClassLoader visible dependency


Ultimately, both Spring and Hibernate will
GO


invokeTransactionalAccess in all frameworks enables OSIV


in RSF OSIV is considerably less messy and more controllable


can
simply target the “alteration” cycle via a RunnableInvoker


In RSF iTA also enables full OTP semantics, again since it properly only
targets the alteration cycle

public

class

GenericDAORunnableInvoker
implements

RunnableInvoker
{


private

CoreGenericDao
genericDAO
;



public

void

setGenericDAO(
CoreGenericDao genericDAO
)

{


this
.
genericDAO

=

genericDAO
;


}




public

void

invokeRunnable(
Runnable torun
)

{


genericDAO
.
invokeTransactionalAccess
(
torun
);


}

}


A RunnableInvoker Conversation


Nig: “Don’t you think people will be upset by the fact
we don’t supply JTA like all the other containers?


Thorton: “Actually I think all the other containers use
JTA because they can’t think up RunnableInvoker”


RIP Andrew Thornton, RSAC instigator and general RSF curmudgeon

(No, he’s not really dead!)



RunnableInvoker/iTA is the natural conclusion of Spring


Transactional thinking, and allows transaction semantics to be


exported out of an API into another ClassLoader with ZERO


code intrusion



Needs to be fleshed out with partners InvokeModifyingAccess


and invokeReadOnlyAccess

RunnableInvoker put to more work


AOP fanatics will recognise RunnableInvoker as the natural
body of an “around” advice


Can also be used as a BeanGuard with the AROUND timing


One use: converting exceptions thrown by JDK conversion
methods into suitable “sleaze” forms


see
uk.org.ponder.util.StaticExceptionConvertingRI


Can also be used as the body of a BeanFetchBracketer


Like a BeanGuard only stronger


brackets the fetch of
ANY RSAC bean


Example: RSF LogonTest sample, supplies a static
declarative strategy for protecting pages from being
viewed by a user who is not logged on

LogonTest App SecurityWrapper

public

class

SecurityWrapper
implements

RunnableInvoker
{


private

ViewParameters
viewparams
;


private

LogonBean
logonbean
;



public

void

setViewParameters(
ViewParameters viewparams
)

{


this
.
viewparams

=

viewparams
;


}


public

void

setLogonBean(
LogonBean logonbean
)

{


this
.
logonbean

=

logonbean
;


}



public

void

invokeRunnable(
Runnable towrap
)

{


if

(
logonbean
.
name

==

null


&&

!(
LogonProducer
.
VIEW_ID
.
equals
(
viewparams
.
viewID
)))

{


throw

new

SecurityException
(
"Cannot view page "

+

viewparams
.
viewID


+

" while not logged on"
);


}


towrap
.
run
();


}

}


<
bean
parent
=
"alterationWrapperParent"
>


<
property
name
=
"value"
>


<
bean
parent
=
"RSACBridgeProxy"
>


<
property
name
=
"targetBeanName"
value
=
"securityWrapper"
/>


</
bean
>


</
property
>


</
bean
>