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