Now you will turn your attention to a more realistic example. You will extend the Agency case study to utilize a Message-driven bean to match advertised jobs to new applicants as they register with the system or when an applicant updates his or her skills or location.
The steps are as follows:
1. |
Write a helper class that creates and sends a message to the jms/applicantQueue containing the applicant's login. |
2. |
Amend the Agency and Register Session beans to call this new method when a new applicant is registered or the applicant's location or skills are changed. |
3. |
Write a Message-driven bean to
|
4. |
Create the jms/applicantQueue queue. |
5. |
This class contains a constructor for the class and two methods—sendApplicant() and close().
The constructor takes two parameters, which are strings representing the JNDI names of the JMS connection factory and the JMS queue.
public MessageSender(String jndiFactory, String jndiQueue) throws JMSException, NamingException { Context context = new InitialContext(); QueueConnectionFactory queueFactory = (QueueConnectionFactory)context.lookup(jndiFactory); queueConnection = queueFactory.createQueueConnection(); queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); queue = (Queue)context.lookup(jndiQueue); queueSender = queueSession.createSender(queue); }
The sendApplicant() method is called by the Agency Session bean when a new applicant registers with the system and the Register Session bean when an existing applicant changes his or her location or job skills. It has two parameters—the applicant's login string and a Boolean denoting if this is a new applicant.
The close() method is called before the application is terminated. It sends a message that lets the container know that no more messages will be sent to the queue and frees-up resources.
The code for the MessageSender in shown in Listing 10.4, it should be very familiar by now.
The following changes are required to AgencyBean.java and RegisterBean.java to call the MessageSender.send() method when a new applicant is registered or the applicant's location or skills are changed.
In both AgencyBean.java and RegisterBean.java create a MessageSender object in the setSessionContext() method..
private MessageSender messageSender; public void setSessionContext(SessionContext ctx) { /* existing code */ messageSender = new MessageSender ( "java:comp/env/jms/QueueConnectionFactory", "java:comp /env/jms/applicantQueue"); }
In the AgencyBean.java file, add code to send a message indicating that a new applicant has registered in the createApplicant() method. The added line is shown in bold in the following code.
public void createApplicant(String login, String name, String email) throws
DuplicateException, CreateException{
try {
ApplicantLocal applicant = applicantHome.create(login,name,email);
messageSender.sendApplicant(applicant.getLogin(),true);
}
catch (CreateException e) {
error("Error adding applicant "+login,e);
}
catch (JMSException e) {
error("Error sending applicant details to message bean "+login,e);
}
In the RegisterBean.java file, change updateDetails() to send a message to indicate that the applicant's details have changed. The added lines are shown in bold in the following code.
public void updateDetails (String name, String email, String locationName, String summary, String[] skillNames) { List skillList; try { skillList = skillHome.lookup(Arrays.asList(skillNames)); } catch(FinderException ex) { error("Invalid skill", ex); // throws an exception return; } LocationLocal location = null; if (locationName != null) { try { location = locationHome.findByPrimaryKey(locationName); } catch(FinderException ex) { error("Invalid location", ex); return; } } applicant.setName(name); applicant.setEmail(email); applicant.setLocation(location); applicant.setSummary(summary); applicant.setSkills( skillList ); try { messageSender.sendApplicant(applicant.getLogin(),false); } catch (JMSException ex) { ctx.setRollbackOnly(); error ("Error sending applicant match message",ex); } }
Compile and deploy this code.
Although this Message-driven bean is significantly larger than your previous example, it does essentially the same thing.
This time, you need to obtain the JNDI InitialContext and use it to obtain references to various Entity beans used in the code.
public void setMessageDrivenContext(MessageDrivenContext ctx) { InitialContext ic = null; try { ic = new InitialContext(); applicantHome = (ApplicantLocalHome)ic.lookup( "java:comp/env/ejb /ApplicantLocal"); } catch (NamingException ex) { error("Error connecting to java:comp/env/ejb/ApplicantLocal:",ex); } try { jobHome = (JobLocalHome)ic.lookup("java:comp/env/ejb/JobLocal"); } catch (NamingException ex) { error("Error connecting to java:comp/env/ejb/JobLocal:",ex); } try { matchedHome = (MatchedLocalHome)ic.lookup( "java:comp/env/ejb/MatchedLocal"); } catch (NamingException ex) { error("Error connecting to java:comp/env/ejb/MatchedLocal:",ex); } }
The ejbCreate() method is blank.
public void ejbCreate(){}
The ejbRemove() cleans up by setting all the references to the Entity beans to null. There are no other resources for you to deallocate.
public void ejbRemove(){ applicantHome = null; jobHome = null; matchedHome = null; }
The onMessage() method is where you will code the algorithm that matches an applicant to advertised jobs. First, you check that onMessage() has received the expected text message. The message contains the applicant's login, which is the primary key on the Applicants table. This primary key is used to obtain the applicant's location and skills in subsequent finder methods.
String login = null; try { if (!(message instanceof TextMessage)) { System.out.println("ApplicantMatch: bad message:" + message.getClass()); return; }
Now, check if this applicant is a new one or if he or she has amended his or her registration. If the applicant has changed his or her details, you need to delete the existing matches stored in the Matched table.
try { login = ((TextMessage)message).getText(); if (! message.getBooleanProperty("NewApplicant")) { matchedHome.deleteByApplicant(login); } } catch (JMSException ex) { error ("Error getting JMS property: NewApplicant",ex); }
Use the login primary key to find the applicant's location using the Applicant Entity bean's finder method.
try { ApplicantLocal applicant = applicantHome.findByPrimaryKey(login); String location = applicant.getLocation().getName();
Next, obtain all the skills that the applicant has registered and store them in an array.
Collection skills = applicant.getSkills(); Collection appSkills = new ArrayList(); Iterator appIt = skills.iterator(); while (appIt.hasNext()) { SkillLocal as = (SkillLocal)appIt.next(); appSkills.add(as.getName()); }
Now you have all the information you need about the applicant. The next step is to start matching the jobs. First find the jobs that match the applicant's location from the Job bean.
Collection col = jobHome.findByLocation(location);
Iterate over this collection finding the skills required for each job.
Iterator jobsIter = col.iterator(); while (jobsIter.hasNext()) { JobLocal job = (JobLocal)jobsIter.next(); Collection jobSkills = job.getSkills();
Now you have a appSkills array containing the applicant's skills and a jobSkills collection containing the skills required for the job. The next task is to find how many of these skills match. This is done by iterating over the jobSkills, and for each jobSkill, searching the appSkills array for a match. When a match is found, the skillMatch counter is incremented.
int skillMatch = 0; Iterator jobSkillIter = jobSkills.iterator(); while (jobSkillIter.hasNext()) { SkillLocal jobSkill = (SkillLocal)jobSkillIter.next(); if (appSkills.contains(jobSkill.getName())) skillMatch++; }
Now see if you have a match. If there was a job skill to match (jobSkills.size() >0) and the applicant did not have any of them (skillMatch == 0), get the next job (continue).
if (jobSkills.size() > 0 && skillMatch == 0) continue;
Otherwise, determine if the applicant has all or just some of the skills.
boolean exact = skillMatch == jobSkills.size();
You are now in a position to update the Matched table. First create the primary key for this table.
MatchedPK key = new MatchedPK(login,job.getRef(),job.getCustomer());
Now all that is left to do is store the applicant and job details in the Matched table.
try { matchedHome.create(key.getApplicant(), key.getJob(), key.getCustomer(), exact); } catch (CreateException ex) {System.out.println( "ApplicantMatch: failed to create matched entry: "+key); }
That is all there is to the bean apart from the exception handling. The full listing of the ApplicantMatch Message-driven bean is shown in Listing 10.5.
Compile this bean.
Run the J2EE RI and use j2eeadmin to create the JMS queue.
j2eeadmin –addJMSDestination jms/applicantQueue queue
Alternatively, use deploytool and select Destinations from the Configure Installation screen and add the queue.
Now deploy the ApplicantMatch Message-driven bean. You will need to add the references to the following entity beans:
Run the Agency application using the appropriate runAll batch file for your operating system. Use the Register screen to add a new applicant, whose location is London and skills are Cigar Maker.
Use the Tables screen to view the contents of the Matched table and check that a row has been added for the new applicant with the following details:
Job—Cigar trimmer
Customer—winston
Exact—false
Add another applicant whose location is also London but and whose skills are Cigar Maker and Critic. Check that this creates a row with the following details in the Matched table:
Job—Cigar trimmer
Customer—winston
Exact—true
Change the skills for this second applicant. Remove the Cigar Maker and Critic and add the skill Bodyguard.
Check that the row for this applicant has now been deleted from the Matched table.
If these checks are okay, congratulations! You have successfully deployed the ApplicantMatch Message-driven bean. Of course, you can add or amend other applicants to find other job matches in the system.
18.222.114.28