jsf & cdi - dreamteam @work

Post on 24-Jun-2015

736 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

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

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

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

@mobileLarson@_openknowledge

Der „Klassiker“

Der „Klassiker“

TX

Java EE Web Framework

Java EE Service Framework

Java EE Persistence Framework

Der „Klassiker“

Wo liegt das Problem?

Heterogene Lösungen

eigene DI, Validierung, LifeCycle

Der „Klassiker“

eigene DI, Validierung, LifeCycle

eigene DI, Validierung, LifeCycle

Technology drivesBusiness

Der „Klassiker“

TX

UC: „Current User“ anzeigen

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

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() { ... }

}

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

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

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

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

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

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

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

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

We needHelp!

Der „Klassiker“ revisted

Der „Klassiker“ revisted

Der „Klassiker“ revisted

CDI 1.0

CDI 1.0 ...aus 10.000 m

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

> 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

CDI Kickstart> LifeCycle Management & Scoping

> Scope sensitive Injection> Automatisches Cleanup

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

> Built-in Pseudo Scopes> Dependant, Singleton

Request Scope

@RequestScopedclass MyBeanA

@RequestScopedclass MyBeanB

@RequestScopedclass MyModel

@RequestScopedclass MyModel

@Inject

@Inject@RequestScopedclass MyBeanB

@Inject

Request 2

Request 1

Session Scope

Request 2

@RequestScopedclass MyBeanA

@RequestScopedclass MyBeanB

@SessionScopedclass MyModel

@Inject

Request 1

@RequestScopedclass MyBeanB

@Inject

@Inject

Session

@SessionScopedclass MyBeanA

@Inject@RequestScopedclass MyModel

Spezialfall

@SessionScopedclass MyBeanA

Proxy forclass MyModel

@Inject

@RequestScopedclass MyModel

Injection Target Contextual Reference Contextual Instance

businessMethod() Lookup or create

return

businessMethod()

returnreturn

Spezialfall

Dependent Scope (Default)

@RequestScopedclass MyBeanA

@SessionScopedclass MyBeanB

@Dependentclass MyModel

@Dependentclass MyModel

@Inject

@Inject

Request 1

> KEIN Proxy und Default!

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“

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“

CDI Kickstart @SessionScoped public class AuthenticationController

implements Serializable {

private User authenticatedUser;

public String authenticate() { ... }

public User getAuthenticatedUser() { return authenticatedUser; } }

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

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

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}

CDI Kickstart

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

Self-made Qualifier

> 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

JSF & CDI: Dreamteam @Work

JSF & CDI: Dreamteam @Work

Refactoring - Step 1

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

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

CDI @Work> Refactoring - Step 1: CDI only

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

JSF & CDI: Dreamteam @Work

JSF & CDI: Dreamteam @Work

Refactoring - Step 2

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

> via @Named und @Produces Zugriff aus EL

CDI @Work> Refactoring - Step 2: View

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

private User user;

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

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

JSF & CDI: Dreamteam @Work

JSF & CDI: Dreamteam @Work

Refactoring - Step 3

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

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

CDI @Work> Refactoring - Step 3: Split UseCase

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

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

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

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

JSF & CDI: Dreamteam @Work

JSF & CDI: Dreamteam @Work

Refactoring - Step 4

Der „Klassiker“

Der „Klassiker“

TX

Der „Klassiker“TX

> via @Interceptor UseCase Transaktion

CDI @Work> Refactoring - Step 4:

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

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

Transactional Annotation

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

? Transactional Annotation

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

JSF & CDI: Dreamteam @Work

JSF & CDI: Dreamteam @Work

Refactoring - Step 5

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

> via @ConversationScoped Wizard

CDI @Work> Refactoring - Step 4:

CDI @Work

@RequestScopedclass MyBeanA

@ConversationSopedclass MyWizard

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

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

@RequestScopedclass MyBeanB

@Inject

@Inject

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 }

}

Der „Klassiker“ refactored

TX

Der „Klassiker“ refactored

TX

Der „Klassiker“ refactoredTX

Mis

Pie ces

sing

Bessere Conversationen

Built-in Transaktionen

weitere Scopes

typesafe Navigation

und vieles mehr ...

Missing Pieces

top related