340 likes | 492 Views
JMS as XML and Object-Based Messaging Integration Infrastructure. Michael Wynholds Founder Carbon Five mike@carbonfive.com. THIS PRESENTATION. Basics of JMS – 10% Types of integration – 50% Integrate system components Integrate dependent objects in EJBs Message payloads – 20%
E N D
JMS as XML and Object-Based Messaging Integration Infrastructure Michael Wynholds Founder Carbon Five mike@carbonfive.com
THIS PRESENTATION • Basics of JMS – 10% • Types of integration – 50% • Integrate system components • Integrate dependent objects in EJBs • Message payloads – 20% • Java objects • XML • Q & A – 20%
WHAT IS JMS? • Java Messaging Service • Java API for enterprise messaging • Non-Java implementations available • Multiple messaging paradigms • Point-to-point • Publish/Subscribe
REAL WORLD EXAMPLE #1 Major Components vs. Sub-Components
INTEGRATING SYSTEM COMPONENTS • Major components • Crucial serial functionality • Affects user experience • Example: shopping basket • Sub-components • Possibly crucial functionality • Slight delays acceptable • Example: Email notification engine
THE WRONG WAY Processes happening serially: • User does something in major component • Waits for sub-component to finish before continuing on through the application
THE WRONG WAY Two things wrong: • Why should the user wait? • What if we decide later we want to perform other non-crucial actions at this time?
ASYNCHRONOUS • Fire and continue • Can still have guaranteed delivery • Sub-components can be moved to other machines
LOOSE COUPLING • Determine significant events • Major components publish • Sub-components subscribe • Addition or modification of sub-component requires no change to major component
EXAMPLE:EMAIL NOTIFICATION • Pet Store • What are significant events? • User logs in • User status is changed • User purchases pet • Shipment is delayed (from another publisher)
SIGNIFICANT EVENT XML <event type="user-status-change"> <context> <timestamp>2001-03-17 12:42:06 PST</timestamp> <user> <first-name>Guy</first-name> <last-name>Incognito</last-name> <email>guy@coolpets.com</email> <status>ADMINISTRATOR</status> </user> </context> <user> <first-name>Michael</first-name> <last-name>Wynholds</last-name> <email>mike@carbonfive.com</email> <status>REGULAR</status> </user> <new-status>PREFERRED</new-status> </event>
NOTIFICATION ENGINE CODE private void init() throws NamingException, JMSException { Context ctx = JNDIUtil.getContext(); String selector = new StringBuffer() .append("type = ").append(MessageTypes.CARBONFIVE_EVENT) .toString(); tconFactory = (TopicConnectionFactory) ctx.lookup(JMS_FACTORY); tcon = tconFactory.createTopicConnection(); tsession = tcon.createTopicSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); try { topic = (Topic) ctx.lookup(TOPIC_NAME); } catch (NamingException ne) { topic = tsession.createTopic(TOPIC_NAME); ctx.bind(TOPIC_NAME, topic); } tsubscriber = tsession.createSubscriber(topic, selector, false); tsubscriber.setMessageListener(this); tcon.start(); }
NOTIFICATION ENGINE CODE public void onMessage(javax.jms.Message msg) { synchronized (this) { int type = msg.getIntProperty("type"); Event event = (Event) ((ObjectMessage) msg).getObject(); switch (msg.getIntProperty("type")) { case MessageTypes.USER_STATUS_CHANGE: Email email = generateEmail((UserStatusChangeEvent) event); email.send(); break; default: break; } } }
NOTIFICATION ENGINE CODE private Email generateEmail(UserStatusChangeEvent event) { Email email = new Email(); email.setTo(event.getUser().getEmail()); email.setFrom("administrator@carbonfive.com"); email.setSubject("Your status has changed"); email.setBody(getBody()); return email; }
MESSAGE SENDER CODE private void init() throws NamingException, JMSException { Context ctx = JNDIUtil.getContext(); tconFactory = (TopicConnectionFactory) ctx.lookup(JMS_FACTORY); tcon = tconFactory.createTopicConnection(); tsession = tcon.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); try { topic = (Topic) ctx.lookup(TOPIC_NAME); } catch (NamingException ne) { topic = tsession.createTopic(TOPIC_NAME); ctx.bind(TOPIC_NAME, topic); } tpublisher = tsession.createPublisher(topic); }
MESSAGE SENDER CODE public void send(Serializable obj, int type) throws JMSException { if (obj == null) { throw new NullPointerException("obj is null"); } ObjectMessage msg = tsession.createObjectMessage(); msg.setObject(obj); msg.setIntProperty("type", type); tpublisher.publish(msg); msg = null; // gc }
USER STATUS CHANGE CODE private void handleStatusChange(ServletRequest request) { HttpSession session = request.getSession(true); User user = UserManager.getUserById(request.getParameter("user_id")); User me = session.getCurrentUser(); UserStatusChangeEvent event = new UserStatusChangeEvent(); EventContext ctx = new EventContext(); ctx.setUser(me); ctx.setTimestamp(new Date()); event.setContext(ctx); event.setUser(user); event.setNewStatus(request.getParameter("new_status")); MessageSender.send(event, MessageTypes.USER_STATUS_CHANGE_EVENT); user.setStatus(request.getParameter("new_status")); }
REAL WORLD EXAMPLE #2 Entity EJB persistence optimization
ENTITY EJB PERSISTENCE • Entity EJB guarantees synchronization with persistent store • Overhead in EJB container • Coarse-grained Entity Beans are better • isModified() method often used to optimize • So what’s the problem?
THE PROBLEM • Multiple beans modify same dependent object • Bean instance may be on separate machines
SOLUTION #1 • Make dependent object an EJB • No longer coarse-grained • EJB overhead takes toll • Multiple machine problem not fixed
SOLUTION #2 • Don’t use isModified() • Slooooooowwwwwww…. • Every getter hits the database • Something may have changed • But most of the time, nothing has changed
SOLUTION #3 • JMS-based isModified() • Dependent object publishes to topic when a setter is called • Entity Beans subscribe to events corresponding to their dependent objects • Efficient • Spans multiple machines
DEPENDENT OBJECT CODE public class Thing { private String name; public void setName(String name) { this.name = name; fireModificationEvent(this.getClass().getName(), this.hashCode()); } }
EJB isModified() private void init() throws NamingException, JMSException { Context ctx = JNDIUtil.getContext(); String selector = new StringBuffer() .append(“class = ”) .append(Thing.class.getName()) .toString(); tconFactory = (TopicConnectionFactory) ctx.lookup(JMS_FACTORY); tcon = tconFactory.createTopicConnection(); tsession = tcon.createTopicSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); try { topic = (Topic) ctx.lookup(TOPIC_NAME); } catch (NamingException ne) { topic = tsession.createTopic(TOPIC_NAME); ctx.bind(TOPIC_NAME, topic); } tsubscriber = tsession.createSubscriber(topic, selector, false); tsubscriber.setMessageListener(this); tcon.start(); }
EJB isModified() public void onMessage(javax.jms.Message msg) { synchronized (this) { ModEvent event = (ModEvent) ((ObjectMessage) msg).getObject(); if (Thing.class.getName().equals(event.getClassName())) { if (this.thing.hashCode == event.getHashCode()) { this.setModified(true); } } } }
ISSUES • Class-based, not object-based events • May receive many messages when using common objects • hashCode() method not always unique • Just means you do a database hit • Must implement hashCode() in objects to work across machines • Can use hash code in selector • Which is more efficient? • I don’t know.
MESSAGE PAYLOADS • Objects • Rule of thumb: Use objects when you can use objects. • XML • Rule of thumb: Use XML when you need to use XML.
OBJECT MESSAGES • Easy to create / use • Little processing overhead • No need for XML tools • Better within same JVM or when JVM is guaranteed • Full functionality of Java inside message
XML MESSAGES • Can be much smaller size • Better for high-traffic distributed systems • Potential direct interaction with receiving systems • Producer or consumer may be non-Java JMS client • No class sync needed
REFERENCES • http://www.carbonfive.com/oreilly Contains links to this and other Carbon Five presentations.