Spring in to Seam
Transcript of Spring in to Seam
Spring into SeamStacking the deck by integrating Spring beans and Seam components
Dan Allen
TetraTech, Inc.
4302
Who am I?
> Author of Seam in Act ion
> Published art icles:
– Seamless JSF (developerWorks)
– Spring into Seam (JavaWorld)
> Commit ter on the JBoss Seam Project
> Software consultant
> Linux and open source advocate
> http:/ / www.mojavelinux.com
3
AGENDA
> Why integrate?
> POJO integrat ion
> EL integrat ion ("Poor man's integrat ion")
> Spring- Seam hybrid components
> Inject ing Seam components into Spring beans
> Persistence context propagat ion and unif ied transact ions
4
AGENDA
> Why integrate?> POJO integrat ion
> EL integrat ion ("Poor man's integrat ion")
> Spring- Seam hybrid components
> Inject ing Seam components into Spring beans
> Persistence context propagat ion and unif ied transact ions
5
Why integrate?
> Applications are typically heterogeneous
– Shaped by their history; reflect expert ise of developers
> Extended inventory of unique functionality
– Seam Perfect compliment to JSF, making it a compelling web framework choice Makes EL ubiquitous; stretches across ent ire stack Introduces real- world variable scopes (conversat ion, business process) Scopes the persistence context properly and manages global transact ions
– Spring Advanced AOP (AspectJ, Spring AOP) Lightweight and Java EE remoting (provides both client and endpoint) Proxy factory beans (e.g. JAX- RPC, JAX- WS, JMS, MBean) Template classes (e.g. JdbcTemplate, HibernateTemplate, JmsTemplate)
> Staged migration
> Different philosophies: stateful vs stateless; annotations vs XML
6
AGENDA
> Why integrate?
> POJO integration> EL integrat ion (aka "Poor man's integrat ion")
> Spring- Seam hybrid components
> Inject ing Seam components into Spring beans
> Persistence context propagat ion and unif ied transact ions
7
Developing with POJOs> The pillar of Spring
> Seam encourages it , but not to such an extreme
> Container agnostic classes
– No imposed interfaces
– No environment- dependent lookups
– Can be tested easily in isolat ion
> Dependency information stored in metadata
– Annotations
– XML
> Classes can jump containers
> Injected object can come from anywhere
– Can even be a proxy
8
POJO integration> Register POJO as Seam component
– Declare in Seam component descriptor (e.g. components.xml)
– Assign dependencies using component configuration
< component name= "tournamentManager" scope= "APPLICATION" class= "...business.impl.TournamentManagerImpl" startup= "true"> < property name= "tournamentDao"> #{tournamentDao}< / property>< / component>
> Just like a Spring bean definit ion, but Seam manages component
> Useful for using classes from a third- party POJO library
> Does not benefit from Spring services
> May even have to emulate some Spring behavior (e.g. Init ializingBean)
References a Seam component
9
AGENDA
> Why integrat ion?
> POJO integrat ion
> EL integration ("Poor man's integration")> Spring- Seam hybrid components
> Inject ing Seam components into Spring beans
> Persistence context propagat ion and unif ied transact ions
10
EL integration ("Poor man's integration")
> Uses exist ing Spring beans and configuration
> EL acts as adapter layer
– Keys on the id (or name) of the Spring bean
> Good place to start integration, may never outgrow it
< bean id= "tournamentManager" class= "...business.impl.TournamentManagerImpl"> < property name= "tournamentDao" ref= "tournamentDao"/ >< / bean>
public interface TournamentManager { public List< Tournament> getUpcomingTournaments(); public boolean addSponsorshipLevel(SponsorshipLevel); / / ...}
11
EL resolvers
> By default, EL looks for a JSF managed bean
> Seam extends EL to look for a Seam component
> Spring can also extend EL to look for a Spring bean
– Standard approach for integrating JSF with Spring
> Primarily used to resolve expression root, but Seam takes EL further
#{beanName.propertyName}
JSF container Seam container Spring container
12
The Spring EL resolver at work
13
Registering the Spring resolver (1)
> Two options, depending on what is available
> Registered in JSF configuration f ile (/ WEB- INF/ faces- config.xml)
> Variable resolver: JSF > = 1.1 and Spring > = 1.1
< faces- config> < application> < variable- resolver> org.springframework.web.jsf.DelegatingVariableResolver < / variable- resolver> < / application>< / faces- config>
14
Registering the Spring resolver (2)
> Two options, depending on what is available
> Registered in JSF configuration f ile (/ WEB- INF/ faces- config.xml)
> EL resolver: JSF > = 1.2 and Spring > = 2.5
< faces- config> < application> < el- resolver> org.springframework.web.jsf.el.SpringBeanFacesELResolver < / el- resolver> < / application>< / faces- config>
15
Which is better, the EL or variable resolver?
EL resolver
> Supports binding expressions
– Assign value to property
– Invoke method
> Pluggable resolver mechanism
– Has access to whole expression
– Does not require JavaBean naming conventions
> Hooks into JBoss EL
– Parameterized expressions
– "Magic" propert ies (e.g. size())
> Works as a standalone API
Variable resolver
> Can only resolve expression root(i.e. #{beanName.propertyName})
> Only resolves property value
> Tied to runtime JSF FacesContext
The verdict: EL
16
Poor man's data access: Indirect method
< h3> Upcoming Tournaments< / h3>< h:dataTable var= "_tournament" value= "#{upcomingTournaments}"> < h:column> < f:facet name= "header"> Name< / f:facet> #{_tournament.name} < / h:column> ...< / h:dataTable>
@Name("tournamentList")public class TournamentListAction { @In("#{tournamentManager}") private TournamentManager tournamentMgr;
@Factory(value = "upcomingTournaments", scope = PAGE) public List< Tournament> loadUpcomingTournaments() { return tournamentMgr.getUpcomingTournaments(); }}
injected priorto method call
17
Poor man's data access: Direct method
< h3> Upcoming Tournaments< / h3>< h:dataTable var= "_tournament" value= "#{upcomingTournaments}"> < h:column> < f:facet name= "header"> Name< / f:facet> #{_tournament.name} < / h:column> ...< / h:dataTable>
> getUpcomingTournaments() method treated as bean property
> Factory protects method from redundant use
> Putt ing factory result in PAGE scope makes it available on JSF postback
< components> < factory name= "upcomingTournaments" scope= "PAGE" value= "#{tournamentManager.upcomingTournaments}"/ >< / components>
18
Poor man's invoke action: Indirect method
< h:form> Add Sponsorship Level Name: < h:inputText value= "#{sponsorshipLevel.name}"/ > < h:commandButton action= "#{sponsorshipLevelAction.save}" value= "Save"/ >< / h:form> @Name("sponsorshipLevelAction")
public class SponsorshipLevelAction { @In("#{tournamentManager}") private TournamentManager tournamentMgr;
@In private SponsorshipLevel sponsorshipLevel;
public boolean save() { return tournamentMgr.addSponsorshipLevel(sponsorshipLevel); }}
injected priorto method call
19
Poor man's invoke action: Direct method
< h:form> Add Sponsorship Level Name: < h:inputText value= "#{sponsorshipLevel.name}"/ > < h:commandButton act ion= "#{tournamentManager.addSponsorshipLevel(sponsorshipLevel)}" value= "Save"/ >< / h:form>
> Method binding expression only works with EL resolver (not variable resolver)
> Leverages JBoss EL parameterized binding extension
Seam = EJB 3 + JSF = Spring + JSF
20
Going beyond poor man's integration
Basic improvement
> Eliminate need to use EL notation in @In annotation
– @In("#{tournamentManager}")
To keep your Spring beans isolated, stop here. Otherwise, you may want to...
> Create Spring- Seam hybrid components
– Store Spring beans in Seam contexts
– Add functionality provided by Seam interceptors to Spring beans Biject ion, declarat ive conversation boundaries, events, etc.
> Allow Spring beans to consume (stateful) Seam components
> Let Seam manage the persistence context for Spring
Overall goal: Make integration more transparent and effective
21
AGENDA
> Why integrate?
> POJO integrat ion
> EL integrat ion (aka "Poor man's integrat ion")
> Spring- Seam hybrid components> Inject ing Seam components into Spring beans
> Persistence context propagat ion and unif ied transact ions
22
Spring- Seam hybrid components
> Has dual cit izenship – Spring bean and Seam component
– Spring bean is wrapped as Seam component
– Requires that bean is looked up through Seam
> Works much the same as the Seam EJB 3 integration
> Not dependent on EL
> Seam method interceptors are applied to Spring bean
– Can be disabled
> Spring bean can be stored in a Seam context
– Default context is STATELESS (i.e. pass- through)
– Remaining Seam contexts act as Spring custom scopes
> Can result in chicken/ egg problem during startup
– Safest bet: Use Seam to boot Spring
– Handled by built- in Seam component
23
Bootstrapping Spring from Seam
> Step 1: Add Spring namespace to Seam component descriptor
< components xmlns= "http:/ / jboss.com/ products/ seam/ components" ... xmlns:spring= "http:/ / jboss.com/ products/ seam/ spring" xsi:schemaLocation= " ... http:/ / jboss.com/ products/ seam/ spring http:/ / jboss.com/ products/ seam/ spring- 2.0.xsd">< / components>
> Step 2: Declare built- in Spring context loader component
< spring:context- loader/ >
> Step 3: Identify locations of Spring bean configuration f iles(not required if using / WEB- INF/ applicationContext.xml)
< spring:context- loader config- locations= "/ WEB- INF/ spring- beans.xml,classpath:spring- beans-dao.xml"/ >
24
Seam XML namespace for Spring
> Seam registers an XML namespace handler with Spring configuration f ile
> Receives callback when encountering Seam XML element
> Augments bean definit ions or contributes definit ions to Spring context
> Facilitates two way communication between Seam and Spring
Seam Spring
25
Registering the Seam XML namespace
> Adds a Seam XML vocabulary to Spring configuration f ile
< beans xmlns= "http:/ / www.springframework.org/ schema/ beans" ... xmlns:seam= "http:/ / jboss.com/ products/ seam/ spring- seam" xsi:schemaLocation= " ... http:/ / jboss.com/ products/ seam/ spring- seam http:/ / jboss.com/ products/ seam/ spring- seam- 2.0.xsd">< / beans>
26
Elements in the Seam XML namespace
> < seam:component>Creates a Seam- Spring hybrid component
> < seam:instance>Defines a Spring factory bean that retrieves a Seam component instanceUsed to inject Seam component instance into property of Spring bean
> < seam:configure- scopes>Infuses Seam container with Seam scopes
27
Spring- Seam hybrid component creation process
28
Optional attributes for hybrid component
> scope – Stores Spring bean (or proxy) in Seam context
< bean id= "tournamentManager" class= "..." scope= "prototype"> < seam:component scope= "CONVERSATION" ... / >< / bean>
> auto- create – Automatically resolve Spring bean if not present in Seam context
< seam:component auto- create= "true" ... / >
> intercept – Controls whether Seam interceptors are applied to Spring bean
< seam:component intercept= "false" ... / >
> name – Makes the Seam component name different from Spring bean id
< seam:component name= "tournamentMgr" ... / >
29
Making Spring proxies compatible with Seam
> Two approaches to proxying:
– JDK dynamic proxy – implements interfaces of bean class
– Cglib proxy – subclass of bean class; can be cast to bean class
> JDK dynamic proxy is default choice in Spring
> Seam needs access to bean class
– Cannot wrap JDK dynamic proxies
– Need to tell Spring to use Cglib proxy
< < interface> >TournamentManager
TournamentManagerJDKProxyTournamentManagerImpl
TournamentManagerCglibProxy
30
Reconfiguring Spring- applied interceptors
> AOP advice configuration:
< aop:config proxy- target- class= "true">
< aop:advisor id= "tournamentManagerTx" advice- ref= "defaultTxAdvice"
pointcut= "execution(* org.open18.business.TournamentManager.*(..))"/ >
< / aop:config>
> AspectJ annotation- based configuration:
< aop:aspectj- autoproxy proxy- target- class= "true"/ >
> Java 5 annotation- based transaction configuration:
< tx:annotation- driven proxy- target- class= "true"/ >
31
Reconfiguring a proxy factory bean
> Two ways to switch to a Cglib proxy:
– Set proxyTargetClass property to true
– Don't specify proxyInterfaces property
> Also need to tell Seam the class of the target object using the class attribute
< bean id= "tournamentManager" class= "...transaction.interceptor.TransactionProxyFactoryBean"> < seam:component class= "...business.impl.TournamentManagerImpl"/ > < property name= "proxyInterfaces" value= "org.open18.business.TournamentManager"/ > < property name= "proxyTargetClass" value= "true"/ > < property name= "target" ref= "tournamentManagerTarget"> < property name= "transactionAttributes"> ...< / property>< / bean>
32
Spring- Seam hybrid component shorthand
> Leverages Spring custom scopes (Spring > = 2.0)
> Alternative to singleton and prototype
> Scopes are enum constants in org.jboss.seam.ScopeType prefixed with "seam."
> Give up some flexibility
> Step 1: Register Seam scopes
< seam:configure- scopes/ >
> Step 2: Assign Seam scope to Spring bean
< bean id= "tournamentManager" class= "..." scope= "seam.CONVERSATION"> < / bean>
> Step 3: Enable auto- create behavior (optional)
< seam:configure- scopes default- auto- create= "true"/ >
No longer need < seam:component> tag
33
AGENDA
> Why integrate?
> POJO integrat ion
> EL integrat ion ("Poor man's integrat ion")
> Spring- Seam hybrid components
> Injecting Seam components into Spring beans> Persistence context propagat ion and unif ied transact ions
34
Giving back to Spring
> Inject Seam component instance into property of Spring bean
– Declare < seam:instance> as Spring bean property value
– Use < seam:instance> to create Spring bean alias
> Variants:
– Component name:< seam:instance name= "sponsorshipValidator"/ >
– EL value expression:< seam:instance name= "#{tournamentHome.instance}"/ >
> Two containers work together to "wire" a bean
> Required to inject a Spring- Seam hybrid component
35
When stateless and stateful collide
> Spring advocates stateless architecture
> Seam advocates stateful architecture
> Risks of mixing these two architectures:
– Violat ion of thread safety Act: Singleton puts reference to a thread- bound object in a shared locat ion (field on
class) Consequence: Reference is exposed to concurrent threads
– Scope impedance Act: Object in longer- term scope holds reference to bean in shorter- term scope Consequence: Object in shorter- term scope out lives expected lifet ime
> Caused by:
– Inappropriate use of singletons
– Static injection
– Hard references
36
Singletons and concurrency (or lack thereof)
> Singletons are shared amongst all threads
– May be accessed concurrently
– Methods that access shared data are not thread- safe!
– Seam's biject ion expects object to be single- threaded
> Can synchronize access using the synchronized keyword per method
– Synchronizing introduces bott lenecks and deadlock scenarios
> Concurrency options
– Pooling of singleton using Spring's AbstractPoolingTargetSource
– Seam's @Synchronized annotation (preferred over synchronized keyword) Can detect deadlock and t imeout the call (throws exception) Timeout is configurable
> Spring and Seam both offer scoped proxies to eliminate shared references
37
Adding a layer of separation
> Inject a locator proxy rather than hard reference
< seam:instance name= "componentName" proxy= "true"/ >
> Each t ime property is touched, calls out to Seam container to get instance
> Injected objects are guaranteed to be thread- safe
> Solves scope impedance by always retrieving latest instance
– Only leaves behind the locator after method call
> Slight overhead from recurrent lookups to Seam container
> Avoids introducing bott lenecks
– Method calls don't need to be synchronized
– Object doesn't need to be pooled
38
A comparison of scoped proxies
> Spring bean made "scope aware" using AOP: scoped proxy
– Nest < aop:scoped- proxy/ > inside of bean definit ion
– Prevents scope impedance and thread- safety violat ion
– Intended to be used with session, request, and custom scopes
> Spring's scoped proxy not compatible with Spring- Seam hybrid components!
– Must use < seam:instance proxy= "true"/ > instead
> How they differ:
– Spring proxy is defined on bean definit ion
– Seam proxy is defined at point of injection
39
Making a Spring bean out of a Seam component> Spring- Seam hybrid decorates Spring bean with Seam behavior
> Inverse: Make Seam component appear like a Spring bean
> Use top- level < seam:instance>
– Spring bean id defaults to Seam component name unless overridden
– Definit ions can be isolated in separate file
– Spring can't tell the difference; can use bean "ref" syntax
< seam:instance name= "currentUser" proxy= "true" scope= "SESSION"/ >< seam:instance id= "selectedTournament" name= "#{tournamentHome.instance}" proxy= "true"/ >
< bean id= "tournamentManager" class= "..."> < property name= "currentUser" ref= "currentUser"/ > < property name= "tournament" ref= "selectedTournament"/ >< / bean>
40
Introducing Spring to state
Parameterized EL
Pros:
> Can use POJO as is
> No risk of scope impedance in design
Cons:
> Methods act stat ic; not good OO
> Must be called from EL (JBoss EL)
> Violates encapsulation
< seam:instance>
Pros:
> Does not require use of EL
> Method signatures kept simple
Cons:
> Requires awareness of scopes
> Can cause scope impedance if used w/ o proxy
Use if just testing the integration Use if committed to the integration
41
AGENDA
> Why integrate?
> POJO integrat ion
> EL integrat ion ("Poor man's integrat ion")
> Spring- Seam hybrid components
> Inject ing Seam components into Spring beans
> Persistence context propagation and unified transactions
42
Bring peace to the persistence land
> Persistence management is central to the application
> Only one framework gets to control it
– Both Seam and Spring offer a way
– Seam is f lexible; can consume externally defined configuration
> Not only does Spring not share, it handles the persistence context incorrectly
43
The persistence context in 60 seconds
> Maintains set of managed entit ies retrieved from database; f irst- level cache
– For any persistent id, there is a unique entity instance
> Supports transparent persistence:
– Automatic change detection – Changes to managed entit ies are tracked; synchronized to database when flush occurs (e.g. when transaction ends)
– Persistence by reachability – Persists or updates related ent it ies that are accessible through a mapped property of the original (i.e. @ManyToOne)
– Lazy loading – Related entit ies configured as lazy are fetched on demand
> Persistence context ends when persistence manager (EntityManager) is closed
– Entity instances in persistence context become detached
– Transit ive persistence stops working
Persistence context should remain open as long as entity instances are being used
44
Where Spring gets it wrong
> Treats persistence manager (and context) as subsidiary of the transaction
– Strips it of its value
– Raises odds of encountering an ORM violat ion (lazy- load error, non-unique)
> OpenSessionInViewFilter binds persistence manager to current thread
– Makes the situation even more confusing
– Persistence context st ill ends when request ends; delays problem
45
Using a Spring- configured persistent unit
> Seam resolves the persistence manager factory using an EL value expression
> Use Spring- configured persistence unit (JPA / Hibernate); poor man's integration
applicationContext.xml< bean id= "entityManagerFactory" class= "org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> < property name= "persistenceUnitName" value= "tournamentPU"/ > < property name= "dataSource" ref= "dataSource"/ > < property name= "jpaDialect"> < bean class= "org.springframework.orm.jpa.vendor.HibernateJpaDialect"/ > < / property>< / bean>
components.xml< persistence:managed- persistence- context name= "entityManager"ent ity- manager- factory= "#{entityManagerFactory}" auto- create= "true"/ >
@In EntityManager entityManager
46
Regifting the persistence manager for Spring
> Need to give the Seam- managed persistence context back to Spring
> Spring isn't aware of the switch
applicationContext.xml< bean id= "entityManagerFactorySpring" class= "org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> ...< / bean>< bean id= "entityManagerFactory" class= "org.jboss.seam.ioc.spring.SeamManagedEntityManagerFactoryBean"> < property name= "persistenceContextName" value= "entityManager"/ >< / bean>
components.xml< persistence:managed- persistence- context name= "entityManager"ent ity- manager- factory= "#{entityManagerFactorySpring}" auto-create= "true"/ >
the one Spring will use
47
Visualizing the persistence hand- off
48
How Seam cleans up the mess
> Seam prevents Spring from closing the persistence manager
– Persistence context remains open for at least the whole request
– Open Session in View for free
– Extended across requests if Seam conversation is in use
– No lazy- load exceptions; no need to use merge()
> Spring can transparently inject Seam- managed persistence context into @PersistenceContext property on Spring bean
49
One transaction to rule them all
> Don't want two different transaction managers for one resource
> Addit ionally, Seam wraps each request in two transactions
– One from Restore View phase until after Invoke Applicat ion phase
– One around Render Response phase (read only)
> Once again, use EL to tap into Spring; poor man's integration
applicationContext.xml< bean id= "txManager" class= "org.springframework.orm.jpa.JpaTransactionManager"> < property name= "entityManagerFactory" ref= "entityManagerFactory"/ >< / bean>
components.xml< spring:spring- transaction platform- transaction-manager= "#{txManager}"/ >
must be Seam's version
50
Guice integration on the horizonWe gave you EJB 3...
Next, we gave you Spring...
Now, we give you Guice!
@Name("cafe")@Guicepublic class Cafe { @Inject private Juice juiceOfTheDay; @Inject @Orange private Juice anotherJuice; / / ...
}
public class CafeModule implements Module { public void configure(Binder binder) { binder.bind(Juice.class) .toInstance(new JuiceImpl("Apple Juice", 10)); binder.bind(Juice.class) .annotatedWith(Orange.class) .toInstance(new JuiceImpl("Orange Juice", 12)); }}
< guice:injector name= "cafeInjector"> < guice:modules> < value> ...guice.CafeModule< / value> < / guice:modules>< / guice:injector>< guice:init injector= "#{cafeInjector}"/ >
Injection by type
51
Wrap up
> Spring and Seam are not mutually exclusive
> POJO- based development enables sharing
> Can start small with "Poor man's integration" (EL)
> In full stride, you are using both containers for their strengths
– Bring advanced AOP to Seam
– Leverage Spring's template classes, proxy factories, and exporters
– Seam can handle conversational state
– Seam can manage the persistence context correctly
Dan Allen http:/ / mojavelinux.comTetraTech, Inc. [email protected]