DecoratorとEvent

★Decoratorについて

サンプル1

@Decorator

public class LogDecorator implements Logger {

@Inject

@Delegate // Original object

@Any

private Logger logger;


@Override

public void log(String msg) {

logger.log(timestamp() + ":" + msg);

}

}

サンプル2

public class TicketServiceImpl implements TicketService {

@Override

public Ticket orderTicket(String name) {

...

}

}

import javax.decorator.Decorator;

import javax.decorator.Delegate;

import javax.inject.Inject;

@Decorator

public class TicketServiceDecorator implements TicketService {

@Inject

@Delegate

private TicketService ticketService;


@Inject

private OtherService otherService;


@Override

public Ticket orderTicket(String name) {

Ticket ticket = ticketService.orderTicket(name);

otherService.order(ticket);

return ticket;

}

}

<decorators>

<class>org.example.TicketServiceDecorator</class>

</decorators>

サンプル3

import javax.ejb.ApplicationException;

@ApplicationException

public class AccessDeniedException extends RuntimeException {

public AccessDeniedException(String s) {

super(s);

}

}

import javax.annotation.Resource;

import javax.ejb.SessionContext;

import javax.ejb.Stateless;

import javax.enterprise.inject.Produces;

@Stateless

public class CalculatorBean implements Calculator {

@Produces

@Resource

private SessionContext sessionContext;

@Override

public int add(int a, int b) {

return a + b;

}

}

import javax.decorator.Decorator;

import javax.decorator.Delegate;

import javax.inject.Inject;

import java.util.logging.Logger;

@Decorator

public class CalculatorLogging implements Calculator {

private Logger logger = Logger.getLogger("Calculator");

@Inject

@Delegate

private Calculator calculator;

@Override

public int add(int a, int b) {

logger.info( ... );

return calculator.add(a, b);

}

}

@Decorator

public class CalculatorSecurity implements Calculator {

@Inject

@Delegate

private Calculator calculator;

@Inject

private SessionContext sessionContext;

@Override

public int add(int a, int b) {

if (!sessionContext.isCallerInRole("Manager")) {

throw new AccessDeniedException

(sessionContext.getCallerPrincipal().getName());

}

return calculator.add(a, b);

}

}

<decorators>

<class>xxx.cdi.decorators.CalculatorSecurity</class>

<class>xxx.cdi.decorators.CalculatorLogging</class>

</decorators>

単体テスト

import javax.annotation.security.RunAs;

import javax.ejb.EJB;

import javax.ejb.Stateless;

import javax.ejb.embeddable.EJBContainer;

import java.util.concurrent.Callable;

public class CalculatorTest {

//@EJB ×

@Inject

private Calculator calculator;

@EJB

private ManagerBean manager;

@Test(expected=AccessDeniedException.class)

public void testAddFail() {

calculator.add(4, 6);

}

@Test

public void testAddSuccess() {

final int result = manager.call(new Callable<Integer>() {

public Integer call() {

return calculator.add(4, 6);

}

});

assertEquals(10, result);

}

@Stateless

@RunAs("Manager")

public static class ManagerBean {

public <V> V call(Callable<V> callable) {

try {

return callable.call();

} catch (Exception e) {

throw new RuntimeException(e);

}

}

}

}

★Eventについて

特徴:同期、シングルスレッド

カスタム限定子を静的に指定

@Inject @Man

private Event<String> manEvent;

@Inject @Woman

private Event<String> womanEvent;

public void notify() {

this.manEvent.fire("xxx");

this.womanEvent.fire("xxx");

}

public void event(@Observes @Man String info) {...}

public void event(@Observes @Woman String info) {...}

public void event(@Observes String info) {...} 両方とも

public void event(@Observes @Man @Woman String info) {...} 両方ともない

カスタム限定子を動的に指定

@Inject

private Event<String> event;

public void notify() {

this.event.select(new AnnotationLiteral<Man>() {}).fire("xxx");

}

例外処理

@Inject

private Event<String> event;

public void notify() {

try {

this.event.fire("xxx");

} catch (Exception e) {

System.out.println(e.getMessage());

}

}

public void event(@Observes String info) throws XxxException {

throw new XxxException("...");

}

サンプル1

//Event producer

@Inject

@Any

@MyQualifier

private Event<PrintEvent> event;

public void print() {

event.fire(new PrintEvent(3));

}

//Event observer

public void onPrint(

@Observes

@MyQualifier

PrintEvent event) {

...

}

}

サンプル2

@Inject

@Any

private Event<LoggedEvent> loggedEvent;

public void login(User user) {

LoggedEvent event = new LoggedEvent(user);

if (user.isAdmin()) {

loggedEvent.select(new CustomAdmin()).fire(event);

} else {

loggedEvent.fire(event);

}

}

サンプル3

public class SettingsChanged {

public SettingsChanged(String userName) {

this.userName = userName;

}

private String userName;

...

}

public class MyLoginBean {

private @Inject Event<SettingsChanged> event;

...

public boolean login(String username, String password) {

event.fire(new SettingsChanged(username));

...

}

}

@SessionScoped

public class MyBackingBean {

private Locale userLanguage;

...

public void changeLanguage(@Observes SettingsChanged sc) {

userLanguage = getDefaultLanguage(sc.getUserName());

}

...

}

サンプル4

@Singleton

public class Notifier {

@Inject

private Event<Date> dateEvent;

@Schedule(second = "*", minute = "*", hour = "*")

public void sendDate() {

dateEvent.fire(new Date());

}

}

@Singleton

public class Observer {

private List<Date> dates = new ArrayList<Date>();

public void saveDate(@Observes Date date) {

dates.add(date);

}

public List<Date> getDates() {

return dates;

}

}

★Conditional observers

@Observes(notifyObserver=Reception.ALWAYS)

ALWAYS (default)

IF_EXISTS

@Observes(during=TransactionPhase.IN_PROGRESS)

IN_PROGRESS (default) without regard to the transaction phase

BEFORE_COMPLETION before completion phase of the transaction.

AFTER_COMPLETION after completion phase of the transaction.

AFTER__FAILURE after completion phase of the transaction, only when failure

AFTER_SUCCESS after completion phase of the transaction, only when success