On Github rsmeral / devconf2014
DevConf 2014
Contexts and Dependency Injection
Weld, OWB, CanDI
Type-safe resolution, Scopes
public class UserController { @Inject @User public Repository repo;
@SessionScoped @User class UserRepo implements Repository { ...
class Repositories { @Produces @SessionScoped @User public Repository getUserRepo() {
Interceptors
public class Account { ... @Audited public void changePassword(Credentials c) { ...
@Interceptor @Audited public class AuditInterceptor { @AroundInvoke public logOperation() { ...
Events
public void updatePasswordExpiryDate(@Observes @Changed Credentials) { ...
@Inject @Changed Event<Credentials> changeEvent; public changePassword(Credentials c) { ... changeEvent.fire(c);
SPI for integration with other technologiesSeam, Spring, GWT, ...
Custom scopes@PageScope, @BusinessProcessScope
Provide beans, augment existing beans
Catch system events for deployment phasesbefore/after deployment, per bean, per injection point
Integrations with other frameworks
PicketLink, RestEasy, Agorava, …
zk, SoftwareMill, …
Seam 3, MyFaces CODI
Unite
CDI extension framework
Unite efforts
Apache committersSeam developersother CDI ext. authors
now: 0.6-SNAPSHOTsoon: 1.0
JBoss Seam 3
MyFaces CODI
Other
JBoss Seam 3
MyFaces CODI
Other
JBoss Seam 3
MyFaces CODI
Other
Features for extension authors and users
Exceptions as events
... try { doSomethingNasty(); } catch(TooNastyException ex) { logger.log("something nasty happened"); }
@Inject private Event<ExceptionToCatchEvent> exEvt; ... try { doSomethingNasty(); } catch(TooNastyException ex) { exEvt.fire(new ExceptionToCatchEvent(ex)); }
@ExceptionHandler public class LoggingHandler { void logException(@BeforeHandles ExceptionEvent<Exception> evt) {
@ExceptionHandler public class NastinessHandler { void spank(@Handles ExceptionEvent<TooNastyException> evt) {
Type-safe messages
@MessageBundle public interface AppMessages { @MessageTemplate("Hello, %s!") String hello(String name); @MessageTemplate("{greeting}") String greet(String greeting, String name);
greeting=%s, %s!
@Inject AppMessages msg; ... msg.greet("Hello", "DevConf"); msg.hello("World");
ProjectStage, conditional bean exclusion
@Exclude(exceptIfProjectStage=IntegrationTest.class) public class TestUserRepository { ...
Resource loading
@Inject @ExternalResource("schedule.xml") private InputStream inputStream; @Inject @ExternalResource("api-keys.properties") private Properties apiKeys;
CDI in bean validators
class RegistrationAction { String chosenName; void register() { ... } }
class RegistrationAction { @NotTaken String chosenName; void register() { ... } }
class UserNameValidator implements ConstraintValidator<NotTaken, String> { @Inject UserRepository userRepo; boolean isValid(String s, ConstraintValidatorContext cvc) { return userRepo.findByName(s) == null; } }
public class PersonRepository { private Connection conn; private static final String BY_ID = "SELECT * FROM Person WHERE id = ?"; private static final String BY_NAME = "SELECT * FROM Person WHERE firstName LIKE ? AND lastName LIKE ?'"; private static final String STORE = "INSERT INTO Person(firstName,lastName,birthDate) VALUES(?,?,?)"; private static final String UPDATE = "UPDATE Person SET firstName = ?, lastName = ?, birthDate = ?"; private static final String REMOVE = "DELETE FROM Person WHERE id = ?"; private PreparedStatement findByIdStmt; private PreparedStatement findByNameStmt; private PreparedStatement storeStmt; private PreparedStatement updateStmt; private PreparedStatement removeStmt; public PersonRepository() { try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost/people", "user", "pass"); findByIdStmt = conn.prepareStatement(BY_ID); findByNameStmt = conn.prepareStatement(BY_NAME); storeStmt = conn.prepareStatement(STORE); updateStmt = conn.prepareStatement(UPDATE); removeStmt = conn.prepareStatement(REMOVE); } catch (ClassNotFoundException ex) { Logger.getLogger(PersonRepository.class.getName()).log(Level.SEVERE, null, ex); } catch (SQLException ex) { Logger.getLogger(PersonRepository.class.getName()).log(Level.SEVERE, null, ex); } } public Person find(Long id) { try { findByIdStmt.setLong(1, id); ResultSet rs = findByIdStmt.executeQuery(); if (rs.next()) { String firstName = rs.getString("firstName"); String lastName = rs.getString("lastName"); Date birthDate = new Date(rs.getDate("birthDate").getTime()); return new Person(firstName, lastName, birthDate); } } catch (SQLException ex) { Logger.getLogger(PersonRepository.class.getName()).log(Level.SEVERE, null, ex); } return null; } public List<Person> findByName(String first, String last) { try { findByNameStmt.setString(1, "%" + first + "%"); findByNameStmt.setString(1, "%" + last + "%"); ResultSet rs = findByNameStmt.executeQuery(); List<Person> people = new ArrayList<Person>(); while (rs.next()) { Long id = rs.getLong("id"); String firstName = rs.getString("firstName"); String lastName = rs.getString("lastName"); Date birthDate = new Date(rs.getDate("birthDate").getTime()); people.add(new Person(id, firstName, lastName, birthDate)); } return people; } catch (SQLException ex) { Logger.getLogger(PersonRepository.class.getName()).log(Level.SEVERE, null, ex); } return null; } public Long store(Person p) { try { storeStmt.setString(1, p.getFirstName()); storeStmt.setString(2, p.getLastName()); storeStmt.setDate(3, new java.sql.Date(p.getBirthDate().getTime())); storeStmt.executeUpdate(); return storeStmt.getGeneratedKeys().getLong(1); } catch (SQLException ex) { Logger.getLogger(PersonRepository.class.getName()).log(Level.SEVERE, null, ex); } return null; } public void update(Person p) { try { updateStmt.setString(1, p.getFirstName()); updateStmt.setString(2, p.getLastName()); updateStmt.setDate(3, new java.sql.Date(p.getBirthDate().getTime())); updateStmt.executeUpdate(); } catch (SQLException ex) { Logger.getLogger(PersonRepository.class.getName()).log(Level.SEVERE, null, ex); } } public void remove(Person p) { try { removeStmt.setLong(1, p.getId()); removeStmt.executeUpdate(); } catch (SQLException ex) { Logger.getLogger(PersonRepository.class.getName()).log(Level.SEVERE, null, ex); } } }
@Stateful @ConversationScoped public class PersonRepository { @PersistenceContext(type = PersistenceContextType.EXTENDED) private EntityManager em; public void persist(Person p) { em.persist(p); em.flush(); } public void remove(Person p) { em.remove(p); em.flush(); } public List<Person> getAllPeople() { return em.createQuery("SELECT p FROM Person p").getResultList(); } public List<Person> findByName(String name) { return em.createQuery("SELECT p From Person p WHERE p.name LIKE :name") .setParameter("name", name).getResultList(); } }
@Repository public interface PersonRepository extends EntityRepository<Person, Long> {}
Data repositories made simple
Method name DSL
List<Person> findByNameLikeAndAgeBetweenAndGender( String name, int minAge, int maxAge, Gender gender);
Query as annotation
@Query("SELECT p from Person p where p.birthDate = ?1") Person findByBirthDate(Date d);
Auditing API
Criteria API support
Query Delegate – custom DSL
DTO mapping
Pagination
...
Single dynamic implementation for bean interfaces
public interface PersonQueryService { @QueryMethod("select p from Person p order by p.name") public List<Person> findAllSortedByName(); ...
@PartialBeanBinding @Target(TYPE) @Retention(RUNTIME) @interface QueryService {}
@QueryService public interface PersonQueryService { @QueryMethod("select p from Person p order by p.name") public List<Person> findAllSortedByName(); ...
@QueryService public class QueryServicePartialBean implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String query = method.getAnnotation(QueryMethod.class); ...
Type-safe view-configs
@Folder public interface Pages { @View(name = "home") class Index implements ViewConfig { } @Folder interface Admin extends ViewConfig { @View class Index implements Admin { } } }
Navigation
@Model public interface Pages extends ViewConfig { class Home implements Pages { } class Index implements Pages { } }
@Model public interface Pages extends ViewConfig { class Home implements Pages { } class Index implements Pages { public Class<? extends ViewConfig> actionMethod() { return Pages.Home.class; } } }
@Model public interface Pages extends ViewConfig { class Home implements Pages { } class Index implements Pages { @NavigationParameter(key = "param", value = "#{myBean.property}") public Class<? extends ViewConfig> actionMethod() { return Pages.Home.class; } } }
@Model public interface Pages extends ViewConfig { @View(viewParams = INCLUDE) class Home implements Pages { } class Index implements Pages { @NavigationParameter(key = "param", value = "#{myBean.property}") public Class<? extends ViewConfig> actionMethod() { return Pages.Home.class; } } }
Securing method invocation
@Retention(value = RUNTIME) @Target({TYPE, METHOD}) @Documented @SecurityBindingType public @interface CustomSecurityBinding { }
public void doSomething(Thing thing) { thing.doSomething(); }
@CustomSecurityBinding public void doSomething(Thing thing) { thing.doSomething(); }
@CustomSecurityBinding public void doSomething(Thing thing) { thing.doSomething(); }
@Secures @CustomSecurityBinding public boolean doSecCheck(InvocationContext invCont, @LoggedIn User user) throws Exception { return user.isLoggedIn(); // perform security check }
Securing access to Java classes
@Secured(Class<? extends AccessDecisionVoter>)public interface AccessDecisionVoter extends Serializable { Set<SecurityViolation> checkPermission(AccessDecisionVoterContext context); }
@Secured(CustomAccessDecisionVoter.class) public class SecuredBean { //... }
Integration with JSF module
public interface Pages extends ViewConfig { class Index implements Pages { } interface Admin extends Pages { class Manage implements Admin { } class Home implements Admin { } } }
@Secured(EmployeeAccessDecisionVoter.class) public interface Pages extends ViewConfig { class Index implements Pages { } interface Admin extends Pages { class Manage implements Admin { } class Home implements Admin { } } }
@Secured(EmployeeAccessDecisionVoter.class) public interface Pages extends ViewConfig { class Index implements Pages { } @Secured(AdminAccessDecisionVoter.class) interface Admin extends Pages { class Manage implements Admin { } class Home implements Admin { } } }
Application Security Framework for Java EE applications
Provided features:
authenticating users
authorizing access
managing of users, groups, roles,..
plus much more...
Integrates DeltaSpike Security
The simplest solution for Seam / DeltaSpike based apps
CDI extension frameworkOriginally Seam Social
To deal with Social Mediabased on OAuth
Antoine Sabot-Durandother CDI ext. authors
version: 0.7.0Nov 20, 2013
A generic API to deal with OAuth 1.0a and 2.0 services
Specific API for Twitter, Facebook and LinkedIn
API to retrieve basic user information from a Social Service
SPI to extend Agorava with new modules for new services
1. Declare your OAuth application on the Social Media web site
2. Get OAuth app keys
3. Produce it with a CDI producer:
@ApplicationScoped @Produces @Twitter @OAuthApplication(params = {@Param(name = PREFIX, value = "twitter")}) public OAuthAppSettings twitterSettings;
agorava.properties :
twitter.apiKey = your_key twitter.apiSecret = your_secret_key
OAuth authorization
@Inject OAuthLifeCycleService lifeCycleService; String authURL = lifeCycelService.startDanceFor(Twitter.class, "/icallback.jsf");
Wanna work with user's info?
@Inject @Twitter TwitterUserService userService; public void handleUserInfo() { userService.getProfileId(); userService.getScreenName(); //... }
Send tweet?
@Inject @Twitter TwitterTimelineService tl; public Tweet sendTweet(String msg) { return tl.updateStatus(msg); }
https://git-wip-us.apache.org/repos/asf/deltaspike.githttps://issues.apache.org/jira/browse/DELTASPIKE{users,dev}@deltaspike.apache.org
Slides at https://github.com/rsmeral/devconf2014
Feedback?