jsf & cdi - dreamteam @work

84
JSF & CDI: Dreamteam @Work Lars Röwekamp | CIO New Technologies

Upload: open-knowledge-gmbh

Post on 24-Jun-2015

736 views

Category:

Technology


3 download

DESCRIPTION

Dank CDI-Spezifikation lassen sich JSF-basierte Webanwendungen deutlich eleganter und leichtgewichtiger programmieren. JSF-spezifische Workarounds können durch schichtenneutrale Lösungen ersetzt werden. Die Session zeigt anhand eines Migrationsszenarios, wie in wenigen Schritten aus einer Old-School-JSF-Anwendung eine State of the Art JSF-&-CDI-Anwendung werden kann – Überraschungen inklusive.Speaker: Lars Röwekamp19.04.2012 | 8:30 - 9:45 Uhr | JAX, Mainz

TRANSCRIPT

Page 1: JSF & CDI - Dreamteam @Work

JSF & CDI: Dreamteam @WorkLars Röwekamp |  CIO New Technologies

Page 2: JSF & CDI - Dreamteam @Work

JSF & CDI: Dreamteam @WorkLars Röwekamp |  CIO New Technologies

@mobileLarson@_openknowledge

Page 3: JSF & CDI - Dreamteam @Work

Der „Klassiker“

Page 4: JSF & CDI - Dreamteam @Work

Der „Klassiker“

TX

Page 5: JSF & CDI - Dreamteam @Work

Java EE Web Framework

Java EE Service Framework

Java EE Persistence Framework

Der „Klassiker“

Page 6: JSF & CDI - Dreamteam @Work
Page 7: JSF & CDI - Dreamteam @Work

Wo liegt das Problem?

Page 8: JSF & CDI - Dreamteam @Work

Heterogene Lösungen

Page 9: JSF & CDI - Dreamteam @Work

eigene DI, Validierung, LifeCycle

Der „Klassiker“

eigene DI, Validierung, LifeCycle

eigene DI, Validierung, LifeCycle

Page 10: JSF & CDI - Dreamteam @Work

Technology drivesBusiness

Page 11: JSF & CDI - Dreamteam @Work

Der „Klassiker“

TX

UC: „Current User“ anzeigen

Page 12: JSF & CDI - Dreamteam @Work

Der „Klassiker“

TX

Einen JSF ManagedBean

Controller, bitte.

Und für mich einen EJB Service.

Enitity X mit ID Y,wenn möglich.

UC: „Current User“ anzeigen

Page 13: JSF & CDI - Dreamteam @Work

Der „Klassiker“ @ManagedBean(name=“userController“) @SessionScoped public class UserControllerMB implements Serializable {

private User user; // plus getter and setter

@ManagedProperty(value=“#{authenticationController}“) private authenticationControllerMB authenticationController;

@EJB private UserService userService

@EJB private MailService mailService

... // some more services needed public String create() { .. }

public String askForDeleteConfirmation() { ... }

public String deleteAfterConfirmation() { ... }

}

Page 14: JSF & CDI - Dreamteam @Work

Der „Klassiker“ @ManagedBean(name=“userController“) @SessionScoped public class UserControllerMB implements Serializable {

private User user; // plus getter and setter

@ManagedProperty(value=“#{authenticationController}“) private authenticationControllerMB authenticationController;

@EJB private UserService userService

@EJB private MailService mailService

... // some more services needed public String create() { .. }

public String askForDeleteConfirmation() { ... }

public String deleteAfterConfirmation() { ... }

}

String basiertes IoC

Page 15: JSF & CDI - Dreamteam @Work

Der „Klassiker“ @ManagedBean(name=“userController“) @SessionScoped public class UserControllerMB implements Serializable {

private User user; // plus getter and setter

@ManagedProperty(value=“#{authenticationController}“) private authenticationControllerMB authenticationController;

@EJB private UserService userService

@EJB private MailService mailService

... // some more services needed public String create() { .. }

public String askForDeleteConfirmation() { ... }

public String deleteAfterConfirmation() { ... }

}

String basiertes IoC

Infrastrutur Injection

Page 16: JSF & CDI - Dreamteam @Work

Der „Klassiker“ @ManagedBean(name=“userController“) @SessionScoped public class UserControllerMB implements Serializable {

private User user; // plus getter and setter

@ManagedProperty(value=“#{authenticationController}“) private authenticationControllerMB authenticationController;

@EJB private UserService userService

@EJB private MailService mailService

... // some more services needed public String create() { .. }

public String askForDeleteConfirmation() { ... }

public String deleteAfterConfirmation() { ... }

}

String basiertes IoC

Infrastrutur Injection

Technology Injection

Page 17: JSF & CDI - Dreamteam @Work

Der „Klassiker“ @ManagedBean(name=“userController“) @SessionScoped public class UserControllerMB implements Serializable {

private User user; // plus getter and setter

@ManagedProperty(value=“#{authenticationController}“) private authenticationControllerMB authenticationController;

@EJB private UserService userService

@EJB private MailService mailService

... // some more services needed public String create() { .. }

public String askForDeleteConfirmation() { ... }

public String deleteAfterConfirmation() { ... }

}

String basiertes IoC

Infrastrutur Injection

String basierte Navi

Technology Injection

Page 18: JSF & CDI - Dreamteam @Work

Der „Klassiker“ @ManagedBean(name=“userController“) @SessionScoped public class UserControllerMB implements Serializable {

...

public String create() { if (userService.checkForFreeUsername(user.username)) { userService.create(user); mailService.sendWelcomeMail(user);

User loggedInUser = authenticationController.getLoggedInUser(); if (User.ROLE_TRAINEE.equals(loggedInUser.getRole()) { trackingService.trackAction(TrackAction.USER_CREATED, user); } return “USER_CREATED“; // or userSuccessfulCreated.xhtml } else { return “DUPLICATE_USERNAME“; // or errorDuplicateUserName.xhtml } } } String basierte Navi

Page 19: JSF & CDI - Dreamteam @Work

Der „Klassiker“ @ManagedBean(name=“userController“) @SessionScoped public class UserControllerMB implements Serializable {

...

public String create() { if (userService.checkForFreeUsername(user.username)) { userService.create(user); mailService.sendWelcomeMail(user);

User loggedInUser = authenticationController.getLoggedInUser(); if (User.ROLE_TRAINEE.equals(loggedInUser.getRole()) { trackingService.trackAction(TrackAction.USER_CREATED, user); } return “USER_CREATED“; // or userSuccessfulCreated.xhtml } else { return “DUPLICATE_USERNAME“; // or errorDuplicateUserName.xhtml } } }

Monster UseCase

String basierte Navi

Page 20: JSF & CDI - Dreamteam @Work

Der „Klassiker“ @ManagedBean(name=“userController“) @SessionScoped public class UserControllerMB implements Serializable{

private User user;

... public String askForDeleteConfirmation(User userToDelete) { user = userToDelete; return “USER_READY_FOR_DELETE“; // or userDeleteConfirmation.xhtml }

public String deleteAfterConfirmation() { userService.delete(user); ... // do some more stuff, e.g. sending an email, tracking, ... user = null; return “USER_DELETED“; // or userSuccessfulDeleted.xhtml }

}String basierte Navi

Page 21: JSF & CDI - Dreamteam @Work

Der „Klassiker“ @ManagedBean(name=“userController“) @SessionScoped public class UserControllerMB implements Serializable{

private User user;

... public String askForDeleteConfirmation(User userToDelete) { user = userToDelete; return “USER_READY_FOR_DELETE“; // or userDeleteConfirmation.xhtml }

public String deleteAfterConfirmation() { userService.delete(user); ... // do some more stuff, e.g. sending an email, tracking, ... user = null; return “USER_DELETED“; // or userSuccessfulDeleted.xhtml }

}

Session statt „Conversation“

Scope Mismatch

String basierte Navi

Page 22: JSF & CDI - Dreamteam @Work

We needHelp!

Page 23: JSF & CDI - Dreamteam @Work

Der „Klassiker“ revisted

Page 24: JSF & CDI - Dreamteam @Work

Der „Klassiker“ revisted

Page 25: JSF & CDI - Dreamteam @Work

Der „Klassiker“ revisted

Page 26: JSF & CDI - Dreamteam @Work

CDI 1.0

Page 27: JSF & CDI - Dreamteam @Work

CDI 1.0 ...aus 10.000 m

Page 28: JSF & CDI - Dreamteam @Work

CDI Kickstart

JSR-299 Context & Dependency Injection for the Java EE platform

uses JSR-330 Dependency Injection for Java

CDI für Java EE

Java SE Java EE

Page 29: JSF & CDI - Dreamteam @Work

> DI / IoC lite „Java EE without EJB“

> DI / IoC advanced LifeCycle Management und Scoping

> DI / IoC eXtreme Typensicherheit und lose Koppelung

> DI / IoC open Extension-Mechanismus

CDI Features

Page 30: JSF & CDI - Dreamteam @Work

CDI Kickstart> LifeCycle Management & Scoping

> Scope sensitive Injection> Automatisches Cleanup

> Built-in Scopes> Request, Conversation, Session, Application

> Built-in Pseudo Scopes> Dependant, Singleton

Page 31: JSF & CDI - Dreamteam @Work

Request Scope

@RequestScopedclass MyBeanA

@RequestScopedclass MyBeanB

@RequestScopedclass MyModel

@RequestScopedclass MyModel

@Inject

@Inject@RequestScopedclass MyBeanB

@Inject

Request 2

Request 1

Page 32: JSF & CDI - Dreamteam @Work

Session Scope

Request 2

@RequestScopedclass MyBeanA

@RequestScopedclass MyBeanB

@SessionScopedclass MyModel

@Inject

Request 1

@RequestScopedclass MyBeanB

@Inject

@Inject

Session

Page 33: JSF & CDI - Dreamteam @Work

@SessionScopedclass MyBeanA

@Inject@RequestScopedclass MyModel

Spezialfall

Page 34: JSF & CDI - Dreamteam @Work

@SessionScopedclass MyBeanA

Proxy forclass MyModel

@Inject

@RequestScopedclass MyModel

Injection Target Contextual Reference Contextual Instance

businessMethod() Lookup or create

return

businessMethod()

returnreturn

Spezialfall

Page 35: JSF & CDI - Dreamteam @Work

Dependent Scope (Default)

@RequestScopedclass MyBeanA

@SessionScopedclass MyBeanB

@Dependentclass MyModel

@Dependentclass MyModel

@Inject

@Inject

Request 1

> KEIN Proxy und Default!

Page 36: JSF & CDI - Dreamteam @Work

CDI Kickstart> Typesafe Injection

> @Inject und #{...} via Type und Name > @Qualifer als zusätzliche Metadaten> @Inject @Current User loggedInUser

> @Stereotype als „Meta Annotation“> @Model entspricht @Named @RequestScoped

> @Alternative als „Switch“

Page 37: JSF & CDI - Dreamteam @Work

CDI Kickstart> Typesafe Injection

> @Inject und #{...} via Type und Name > @Qualifer als zusätzliche Metadaten> @Inject @Current User loggedInUser

> @Stereotype als „Meta Annotation“> @Model entspricht @Named @RequestScoped

> @Alternative als „Switch“

Page 38: JSF & CDI - Dreamteam @Work

CDI Kickstart @SessionScoped public class AuthenticationController

implements Serializable {

private User authenticatedUser;

public String authenticate() { ... }

public User getAuthenticatedUser() { return authenticatedUser; } }

Page 39: JSF & CDI - Dreamteam @Work

CDI Kickstart @SessionScoped public class AuthenticationController

implements Serializable {

private User authenticatedUser;

public String authenticate() { ... }

public User getAuthenticatedUser() { return authenticatedUser; } }

@Produces @Current User

@Produces @Current @RequestScoped

Page 40: JSF & CDI - Dreamteam @Work

CDI Kickstart @SessionScoped public class AuthenticationController

implements Serializable {

private User authenticatedUser;

public String authenticate() { ... }

public User getAuthenticatedUser() { return authenticatedUser; } }

Aufruf: @Inject @Current User

@Produces @Current User

@Produces @Current @RequestScoped

Page 41: JSF & CDI - Dreamteam @Work

CDI Kickstart @SessionScoped public class AuthenticationController

implements Serializable {

private User authenticatedUser;

public String authenticate() { ... }

public User getAuthenticatedUser() { return authenticatedUser; } }

Aufruf: @Inject @Current User

@Produces @Current User

@Produces @Current @RequestScoped @Named(“loggedInUser“)

Aufruf: #{loggedInUser}

Page 42: JSF & CDI - Dreamteam @Work

CDI Kickstart

@Qualifier@Target({FIELD, PARAMETER, METHOD, TYPE})@Retention(RUNTIME)public @interface Current { }

Self-made Qualifier

Page 43: JSF & CDI - Dreamteam @Work

> Interceptors > Realisierung orthogonaler Aufgaben („AOP“)> Around Advice unterbricht normalen Ablauf> Caching, Security, Transactions, Performance

> Events & Observer> CDI erlaubt Injection einer Event Source> Event Source „feuert“ eigenes Event> Observer „hört“ auf Event

CDI Kickstart> Lose Koppelung

Page 44: JSF & CDI - Dreamteam @Work

JSF & CDI: Dreamteam @Work

Page 45: JSF & CDI - Dreamteam @Work

JSF & CDI: Dreamteam @Work

Refactoring - Step 1

Page 46: JSF & CDI - Dreamteam @Work

Der „Klassiker“ @ManagedBean(name=“userController“) @SessionScoped public class UserControllerMB implements Serializable {

private User user; // plus getter and setter

@ManagedProperty(value=#{authenticationController}) private authenticationControllerMB authenticationController;

@EJB private Stateless userService

@EJB private Stateless mailService

... // some more services needed public String create() { .. }

public String askForDeleteConfirmation() { ... }

public String deleteAfterConfirmation() { ... }

}

String basiertes IoC

Infrastrutur Injection

Scope Mismatch

String basierte Navi

Technology Injection

Page 47: JSF & CDI - Dreamteam @Work

> via @Inject „technologieneutrale“ Injection> via @Qualifier und „Type“ typesafe Injection> via @Named Zugriff aus EL

CDI @Work> Refactoring - Step 1: CDI only

Page 48: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactored @Named(“userController“) @SessionScoped public class UserController implements Serializable {

private User user; // plus getter and setter

@Inject @Current User private User loggedInUser;

@Inject private UserService userService

@Inject private MailService mailService

... // some more services needed public String create() { .. }

public String askForDeleteConfirmation() { ... }

public String deleteAfterConfirmation() { ... }

}

String basierte Navi

Scope Mismatch

Page 49: JSF & CDI - Dreamteam @Work

JSF & CDI: Dreamteam @Work

Page 50: JSF & CDI - Dreamteam @Work

JSF & CDI: Dreamteam @Work

Refactoring - Step 2

Page 51: JSF & CDI - Dreamteam @Work

Der „Klassiker“

<html ...> <h:body> Vorname: <h:outputText value="#{userController.user.firstname}"/> Name: <h:outputText value="#{userController.user.lastname}"/>

</h:body> </html>

Infrastruktur Injection in der View

Page 52: JSF & CDI - Dreamteam @Work

> via @Named und @Produces Zugriff aus EL

CDI @Work> Refactoring - Step 2: View

Page 53: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactored @Named(“userController“) @SessionScoped public class UserController implements Serializable {

private User user;

@Produces @ResquestScoped @Named("selectedUser") public getUser() { return user; } ... }

Page 54: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactored

<html ...> <h:body> Vorname: <h:outputText value="#{selectedUser.firstname}"/> Name: <h:outputText value="#{selectedUser.lastname}"/>

</h:body> </html>

fachliche Injection in der View

Page 55: JSF & CDI - Dreamteam @Work

JSF & CDI: Dreamteam @Work

Page 56: JSF & CDI - Dreamteam @Work

JSF & CDI: Dreamteam @Work

Refactoring - Step 3

Page 57: JSF & CDI - Dreamteam @Work

Der „Klassiker“ @Named(“userController“) @SessionScoped public class UserController implements Serializable {

... public String create() { if (userService.checkForFreeUsername(user.username)) { userService.create(user); mailService.sendWelcomeMail(user);

User loggedInUser = authenticationController.getLoggedInUser(); if (User.ROLE_TRAINEE.equals(loggedInUser.getRole()) { trackingService.trackAction(TrackAction.USER_CREATED, user); } return “USER_CREATED“; // or userSuccessfulCreated.xhtml } else { return “DUPLICATE_USERNAME“; // or errorDuplicateUserName.xhtml } } }

Monster UseCase

String basierte Navi

Page 58: JSF & CDI - Dreamteam @Work

> via BeanValidation Logik-Validierung (sorry, OT)> via Event und @Observes UseCase Splittung

CDI @Work> Refactoring - Step 3: Split UseCase

Page 59: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactored @Named(“userController“) @SessionScoped public class UserController implements Serializable {

private User user; ... public String create() { userService.create(user); // main use case ... // trigger sub use cases return “USER_CREATED“; // userSuccessfulCreated.xhtml } }

OT: Double usernamecheck via BeanValidation

Page 60: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactored @Named(“userController“) @SessionScoped public class UserController implements Serializable {

@Inject @Created Event<User> userCreatedEventSource;

private User user; ... public String create() { userService.create(user); // main use case userCreatedEventSource.fire(user); // trigger sub use cases return “USER_CREATED“; // userSuccessfulCreated.xhtml } }

OT: Double usernamecheck via BeanValidation

Loosly coupled UseCase via Event

Page 61: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactored public class MailService {

public void sendWelcomeMail(@Observes @Created User newUser) { ... // do some work }

... }

Event Consumer

public class TrackingService {

@Inject @Current User loggedInUser;

public void trackUserCreated(@Observes @Created User newUser) { if (User.ROLE_TRAINEE.equals(loggedInUser.getRole()) { this.trackAction(TrackAction.USER_CREATED, user); } } ... }

Event Consumer

Page 62: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactored public class MailService {

public void sendWelcomeMail(@Observes(during == AFTER_SUCCESS) @Created User newUser) { ... // do some work }

... }

Event Consumer

public class TrackingService {

@Inject @Current User loggedInUser;

public void trackUserCreated(@Observes(during == AFTER_SUCCESS) @Created User newUser) { if (User.ROLE_TRAINEE.equals(loggedInUser.getRole()) { this.trackAction(TrackAction.USER_CREATED, newUser); } } ... }

Event Consumer

Page 63: JSF & CDI - Dreamteam @Work

JSF & CDI: Dreamteam @Work

Page 64: JSF & CDI - Dreamteam @Work

JSF & CDI: Dreamteam @Work

Refactoring - Step 4

Page 65: JSF & CDI - Dreamteam @Work

Der „Klassiker“

Page 66: JSF & CDI - Dreamteam @Work

Der „Klassiker“

TX

Page 67: JSF & CDI - Dreamteam @Work

Der „Klassiker“TX

Page 68: JSF & CDI - Dreamteam @Work

> via @Interceptor UseCase Transaktion

CDI @Work> Refactoring - Step 4:

Page 69: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactored @Named(“userController“) @SessionScoped public class UserController implements Serializable {

...

@Transactional public String create() { userService.create(user); // main use case userCreatedEventSource.fire(user); // Created Event return “USER_CREATED“; // Navigation } }

Transactional UseCase

Page 70: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactored @InterceptorBinding @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface Transactional { public TransactionalType value() ! default TransactionalType.REQUIRED; }

Transactional Annotation

Page 71: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactored @InterceptorBinding @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface Transactional { public TransactionalType value() ! default TransactionalType.REQUIRED; }

? Transactional Annotation

Page 72: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactored @Transactional @Interceptor public class TransactionInterceptor {

@Inject private UserTransaction utx;

@AroundInvoke public Object applyTransaction(InvocationContext ic) throws Throwable {

... // implement utx.begin() ic.proceed(); // call original method ... // implement utx.commit()

} }

Transactional Interceptor

Page 73: JSF & CDI - Dreamteam @Work

JSF & CDI: Dreamteam @Work

Page 74: JSF & CDI - Dreamteam @Work

JSF & CDI: Dreamteam @Work

Refactoring - Step 5

Page 75: JSF & CDI - Dreamteam @Work

Der „Klassiker“ @ManagedBean(name=“userController“) @SessionScoped public class UserControllerMB implements Serializable{

private User user;

... public String askForDeleteConfirmation(User userToDelete) { user = userToDelete; return “USER_READY_FOR_DELETE“; // or userDeleteConfirmation.xhtml }

public String deleteAfterConfirmation() { userService.delete(user); ... // do some more stuff, e.g. sending an email, tracking, ... user = null; return “USER_DELETED“; // or userSuccessfulDeleted.xhtml }

}

Session statt Conversation

Scope Mismatch

Page 76: JSF & CDI - Dreamteam @Work

> via @ConversationScoped Wizard

CDI @Work> Refactoring - Step 4:

Page 77: JSF & CDI - Dreamteam @Work

CDI @Work

@RequestScopedclass MyBeanA

@ConversationSopedclass MyWizard

@Inject Conversation conv; // inside „start“ Methode conv.begin();

// inside „end“ Methode conv.end();

@RequestScopedclass MyBeanB

@Inject

@Inject

Page 78: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactored @Named(“userController“) @ConversationScoped public class UserController implements Serializable{

private User user;

@Inject Conversation conversation; ... public String askForDeleteConfirmation(User userToDelete) { conversation.begin(); user = userToDelete; return “USER_READY_FOR_DELETE“; // or userDeleteConfirmation.xhtml }

public String deleteAfterConfirmation() { userService.delete(user); ... // do some more stuff, e.g. fire “user deleted“ CDI event conversation.end(); return “USER_DELETED“; // or userSuccessfulDeleted.xhtml }

}

Page 79: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactored

TX

Page 80: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactored

TX

Page 81: JSF & CDI - Dreamteam @Work

Der „Klassiker“ refactoredTX

Page 82: JSF & CDI - Dreamteam @Work

Mis

Pie ces

sing

Page 83: JSF & CDI - Dreamteam @Work

Bessere Conversationen

Built-in Transaktionen

weitere Scopes

typesafe Navigation

und vieles mehr ...

Page 84: JSF & CDI - Dreamteam @Work

Missing Pieces