Developing the Agency Case Study Example

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

  • Consume a message on the jms/applicantQueue

  • Look up the applicant's location and skills information

  • Find all the jobs that match the applicant's location

  • For each of these jobs, find those that require the applicant's skills

  • Determine if the applicant has all or just some of the skills

  • Store applicant and job matches in the Matched table

4.
Create the jms/applicantQueue queue.

5.
Deploy the new EJBS; run and test the application.

Step 1—Sender Helper Class

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.

Listing 10.4. MessageSender Helper Class
 1: import javax.naming.*;
 2: import javax.jms.*;
 3:
 4: public class MessageSender {
 5:
 6:     private QueueConnection queueConnection;
 7:     private QueueSession queueSession;
 8:     private QueueSender queueSender;
 9:     private Queue queue;
10:
11:     public MessageSender(String jndiFactory, String jndiQueue)
12:  throws JMSException, NamingException {
13:         Context context = new InitialContext();
14:         QueueConnectionFactory queueFactory =
15:  QueueConnectionFactory)context.lookup(jndiFactory);
16:         queueConnection = queueFactory.createQueueConnection();
17:         queueSession = queueConnection.createQueueSession(false,
18:  Session.AUTO_ACKNOWLEDGE);
19:         queue = (Queue)context.lookup(jndiQueue);
20:         queueSender = queueSession.createSender(queue);
21:     }
22:
23:     public void sendApplicant (String applicant, boolean newApplicant)
24:  throws JMSException {
25:         TextMessage message = queueSession.createTextMessage();
26:         message.setBooleanProperty ("NewApplicant", newApplicant);
27:         message.setText(applicant);
28:         queueSender.send(message);
29:         }
30:
31:     public void close() throws JMSException {
32:          //Send a non-text control message indicating end of messages
33:         queueSender.send(queueSession.createMessage());
34:
35:         queueSender.close();
36:         queueSession.close();
37:         queueConnection.close();
38:     }
39: }
						

Step 2—Agency and Register Session Bean

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.

  1. 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");
    }
    
  2. 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);
        }
    
  3. 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);
        }
    }
    
  4. In both AgencyBean.java and RegisterBean.java, add the following to ejbRemove() to close down the MessageSender.

    try {
            messageSender.close();
        }
        catch (JMSException ex) {
            error("Error closing down the queue",ex);
        }
    
  5. Compile and deploy this code.

Step 3—The Message-Driven Bean

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.

Listing 10.5. Full Listing on ApplicantMatch.java Message-Driven Bean Code
  1: package data;
  2:
  3: import java.util.*;
  4: import javax.ejb.*;
  5: import javax.jms.*;
  6: import javax.naming.*;
  7:
  8: public class ApplicantMatch implements MessageDrivenBean, MessageListener
  9: {
 10:     private ApplicantLocalHome applicantHome;
 11:     private JobLocalHome jobHome;
 12:     private MatchedLocalHome matchedHome;
 13:
 14:     public void onMessage(Message message) {
 15:         String login = null;
 16:         if (!(message instanceof TextMessage)) {
 17:             System.out.println("ApplicantMatch: bad message:" +
 18:  message.getClass());
 19:             return;
 20:         }
 21:         try {
 22:             login = ((TextMessage)message).getText();
 23:             if (! message.getBooleanProperty("NewApplicant")) {
 24:                 matchedHome.deleteByApplicant(login);
 25:             }
 26:         }
 27:         catch (JMSException ex) {
 28:             error ("Error getting JMS property: NewApplicant",ex);
 29:         }
 30:         try {
 31:             ApplicantLocal applicant = applicantHome.findByPrimaryKey(login);
 32:             String location = applicant.getLocation().getName();
 33:             Collection skills = applicant.getSkills();
 34:             Collection appSkills = new ArrayList();
 35:             Iterator appIt = skills.iterator();
 36:             while (appIt.hasNext()) {
 37:                 SkillLocal as = (SkillLocal)appIt.next();
 38:                 appSkills.add(as.getName());
 39:             }
 40:             Collection col = jobHome.findByLocation(location);
 41:             Iterator jobsIter = col.iterator();
 42:             while (jobsIter.hasNext()) {
 43:                 JobLocal job = (JobLocal)jobsIter.next();
 44:                 Collection jobSkills = job.getSkills();
 45:                 int skillMatch = 0;
 46:                 Iterator jobSkillIter = jobSkills.iterator();
 47:                 while (jobSkillIter.hasNext()) {
 48:                     SkillLocal jobSkill = (SkillLocal)jobSkillIter.next();
 49:                     if (appSkills.contains(jobSkill.getName()))
 50:                        skillMatch++;
 51:                 }
 52:                 if (jobSkills.size() > 0 && skillMatch == 0)
 53:                     continue;
 54:                 boolean exact = skillMatch == jobSkills.size();
 55:                 MatchedPK key = new MatchedPK(login,job.getRef(),
 56:  job.getCustomer());
 57:                 try {
 58:                     matchedHome.create(key.getApplicant(),key.getJob(),
 59:  key.getCustomer(), exact);
 60:                 }
 61:                 catch (CreateException ex) {
 62:                     System.out.println("ApplicantMatch: failed to create
 63:  matched entry: "+key);
 64:                 }
 65:             }
 66:         }
 67:         catch (FinderException ex) {
 68:             System.out.println("ApplicantMatch: failed to find applicant
 69:  data: "+login);
 70:         }
 71:         catch (RuntimeException ex) {
 72:             System.out.println("ApplicantMatch: "+ex);
 73:             ex.printStackTrace();
 74:             throw ex;
 75:         }
 76:     }
 77:
 78:     // EJB methods start here
 79:
 80:     public void setMessageDrivenContext(MessageDrivenContext ctx) {
 81:     InitialContext ic = null;
 82:         try {
 83:             ic = new InitialContext();
 84:             applicantHome = (ApplicantLocalHome)ic.lookup(
 85:  "java:comp/env/ejb/ApplicantLocal");
 86:         }
 87:         catch (NamingException ex) {
 88:             error("Error connecting to java:comp/env/ejb/ApplicantLocal:",ex);
 89:         }
 90:         try {
 91:             jobHome = (JobLocalHome)ic.lookup("java:comp/env/ejb/JobLocal");
 92:         }
 93:         catch (NamingException ex) {
 94:             error("Error connecting to java:comp/env/ejb/JobLocal:",ex);
 95:         }
 96:         try {
 97:             matchedHome = (MatchedLocalHome)ic.lookup(
 98:  "java:comp/env/ejb/MatchedLocal");
 99:         }
100:         catch (NamingException ex) {
101:             error("Error connecting to java:comp/env/ejb/MatchedLocal:",ex);
102:         }
103:     }
104:
105:     public void ejbCreate(){
106:     }
107:
108:     public void ejbRemove(){
109:         applicantHome = null;
110:         jobHome = null;
111:         matchedHome = null;
112:     }
113:
114:     private void error (String msg, Exception ex) {
115:         String s = "ApplicantMatch: "+msg + "
" + ex;
116:         System.out.println(s);
117:         throw new EJBException(s,ex);
118:     }
						

Compile this bean.

Step 4—Create the JMS Queue

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.

Step 5—Deploy the EJBS

Now deploy the ApplicantMatch Message-driven bean. You will need to add the references to the following entity beans:

  • applicant— Coded name java:comp/env/ejb/ApplicantLocal

  • applicantSkill— Coded name java:comp/env/ejb/ApplicantSkillLocal

  • job— Coded name java:comp/env/ejb/JobLocal

  • jobSkill— Coded name java:comp/env/ejb/JobSkillLocal

  • matched— Coded name java:comp/env/ejb/MatchedLocal

Step 6—Testing the ApplicantMatch Bean

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.114.28