Reputation: 4272
I wonder if it is possible to do something along these lines:
1) Server side (EJB class)
@Statefull
public class SomeEJB implements SomeEJBRemote {
@Resource
private SessionContext sessionContext;
//(...)
public void someMethod(Record record){
//(...)
//Situation that requires the server to alert all registered listeners
if(specialSituation){
sessionContext.fireEventToListeners(new SomeEvent());
}
//Do something else...
}
}
2) Client side
//(...)
SomeEJBRemote ejb = initialContext.lookup("SomeEJBRemote");
ejb.addListener(this);
void EJBEventListener(SomeEvent event){
System.out.println("EJB server has sent an event!");
}
A) Is there anything in the Java EE specification that allows this?
B) I know that JBoss allows some sort of bidirectional communication, but can I do just like that example?
C) Is there anything like this in OpenEJB (or TOMEE)?
Upvotes: 4
Views: 1704
Reputation: 19368
It's easy to do with an embedded EJB container and an MDB in the client. We have an example that does exactly that.
Check out the monitor
module of this example.
At 10,000 feet this example does the following:
Server-side:
Client-side:
java.awt.SystemTray
So the interesting thing about this technique is that it is fully transactional -- the EntityManager updates and the JMS messages sent are all part of the transaction. If the database update fails, no JMS message will be sent.
Here's 100% of the client code from that example. Doesn't take much to do what's described.
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.awt.AWTException;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;
public class NotificationMonitor {
private static TrayIcon trayIcon;
public static void main(String[] args) throws NamingException, InterruptedException, AWTException, MalformedURLException {
addSystemTrayIcon();
// Boot the embedded EJB Container
new InitialContext();
System.out.println("Starting monitor...");
}
private static void addSystemTrayIcon() throws AWTException, MalformedURLException {
SystemTray tray = SystemTray.getSystemTray();
URL moviepng = NotificationMonitor.class.getClassLoader().getResource("movie.png");
Image image = Toolkit.getDefaultToolkit().getImage(moviepng);
ActionListener exitListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Exiting monitor...");
System.exit(0);
}
};
PopupMenu popup = new PopupMenu();
MenuItem defaultItem = new MenuItem("Exit");
defaultItem.addActionListener(exitListener);
popup.add(defaultItem);
trayIcon = new TrayIcon(image, "Notification Monitor", popup);
trayIcon.setImageAutoSize(true);
tray.add(trayIcon);
}
public static void showAlert(String message) {
synchronized (trayIcon) {
trayIcon.displayMessage("Alert received", message, TrayIcon.MessageType.WARNING);
}
}
}
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "notifications")})
public class NotificationsBean implements MessageListener {
public void onMessage(Message message) {
try {
TextMessage textMessage = (TextMessage) message;
String text = textMessage.getText();
NotificationMonitor.showAlert(text);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
This configures the embedded EJB container. You could do this in code as well.
java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory
Default\ JMS\ Resource\ Adapter=new://Resource?type=ActiveMQResourceAdapter
Default\ JMS\ Resource\ Adapter.BrokerXmlConfig=broker:vm://localhost
Default\ JMS\ Resource\ Adapter.ServerUrl=tcp://localhost:61616
Upvotes: 4
Reputation: 33936
No, there is nothing like this in EJB. I would suggest having the client either listen to a JMS queue/topic. Alternatively, the client could export a listener object to RMI (effectively becoming a server itself), and then pass a reference to server; this technique would require quite a bit more infrastructure.
Upvotes: 1