Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/presumo/mobileagent/Agent.java


1   /**
2    * This file is part of Presumo.
3    *
4    * Presumo is free software; you can redistribute it and/or modify
5    * it under the terms of the GNU General Public License as published by
6    * the Free Software Foundation; either version 2 of the License, or
7    * (at your option) any later version.
8    *
9    * Presumo is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with Presumo; if not, write to the Free Software
16   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17   *
18   *
19   * Copyright (c) 2001 Dan Greff
20   */
21  package com.presumo.mobileagent;
22  
23  import com.presumo.util.log.Logger;
24  import com.presumo.util.log.LoggerFactory;
25  
26  import java.io.BufferedReader;
27  import java.io.InputStreamReader;
28  import java.io.Serializable;
29  
30  import java.util.Vector;
31  
32  import javax.jms.ExceptionListener;
33  import javax.jms.Topic;
34  import javax.jms.TopicConnection;
35  import javax.jms.TopicSession;
36  import javax.jms.TopicSubscriber;
37  import javax.jms.TopicPublisher;
38  import javax.jms.Message;
39  import javax.jms.MessageListener;
40  import javax.jms.ObjectMessage;
41  import javax.jms.JMSException;
42  import javax.jms.Session;
43  
44  
45  /**
46   * A very rough base class for mobile agents thrown together
47   * to support Presumo's test framework.
48   * <p>
49   * Currently your Agent implementation must be in the CLASSPATH 
50   * of the JVM your agent moves to.   
51   * <p>
52   * <note>
53   * It would be relativly trivial to have the Agent run 
54   * against a ClassLoader running over JMS to remove this restriction. 
55   * If you are reading this with interest in using MobileAgents
56   * over JMS contact the project admins at www.presumo.com and
57   * request the feature, or (if you are capable of doing so) 
58   * volunteer to do it yourself and join the project. ;) DTG
59   * </note>
60   *
61   * @author Dan Greff
62   */
63  public abstract class Agent implements Serializable, Runnable
64  {
65    /** Topic name for which all agent JMS traffic runs across. **/
66    public static final String AGENT_TOPIC = "MobileAgentAdmin";
67    
68    /** Message property name for Runner targets a Agent is sent to. **/
69    public static final String RUNNER_PROP = "RunnerTargets";
70    
71    /** Message property name used to indicate a Runner identifier. **/
72    public static final String RUNNER_NAME = "RunnerName";
73    
74    /** Message property name used to indicate the type of message. **/
75    public static final String MESSAGE_TYPE = "AgentMessageType";
76    
77    public static final int AGENT_MOVE     = 0;
78    public static final int AGENT_START    = 1;
79    public static final int AGENT_STOP     = 2;
80    public static final int RUNNER_QUERY   = 3;
81    public static final int QUERY_RESPONSE = 4;
82  
83    
84    /** Means by which the agent communicates witht he JMS layer **/
85    protected transient TopicConnection connx;
86    protected transient TopicSession    agentSession;
87    private   transient Topic           agentTopic;
88  
89      /////////////////////////////////////////////////////////////////////////
90     // Constructors                                                        //
91    /////////////////////////////////////////////////////////////////////////
92    
93    /**
94     * Default constructor needed only for deserialization.  When subclassing
95     * agent you must make sure <code>agentSession</code> is set.
96     */
97    public Agent()
98    {
99      logger.entry("Agent");
100     logger.exit("Agent", this);
101   }
102   
103   /**
104    * Preferred Constructor to invoke when subclassing.  If you do not invoke
105    * this Constructor you must ensure that <code>agentSession</code> is set.
106    */
107   public Agent(TopicConnection connx)
108   {
109     logger.entry("Agent", connx);
110     setConnection(connx);
111     logger.exit("Agent", this);
112   }
113   
114     ///////////////////////////////////////////////////////////////////////////
115    // Public Methods                                                        //
116   ///////////////////////////////////////////////////////////////////////////
117 
118   /**
119    * Move the agent to the specified <code>AgentRunner</code>.
120    */
121   public void moveTo(String runnerName) throws JMSException
122   {
123     logger.entry("moveTo", runnerName);
124     moveTo(new String [] { runnerName });
125     logger.exit("moveTo");
126   }
127   
128   /**
129    * Broadcast the agent to all the specivied <code>AgentRunner</code>.
130    */
131   public synchronized void moveTo(String [] runnerNames) 
132     throws JMSException
133   { 
134     logger.entry("moveTo", runnerNames);
135     TopicPublisher publisher = agentSession.createPublisher(agentTopic);
136     try {
137       Message msg = createRunnerMoveMessage(runnerNames);      
138       publisher.publish(msg);
139     } finally {
140       publisher.close();
141     }
142     logger.exit("moveTo");
143   }
144   
145   
146   /**
147    * Get all <code>AgentRunner</code>'s available to run Agents.
148    */
149   public synchronized String [] getAvailableRunners(long timeout) 
150     throws JMSException
151   {
152     logger.entry("getAvailableRunners", new Long(timeout));
153 
154     TopicSubscriber sub = null;
155     TopicPublisher  pub = null;
156     Vector runners      = new Vector();
157 
158     try {
159       sub = createQueryResponseReceiver();
160       pub = agentSession.createPublisher(agentTopic);
161       pub.publish( createQueryMessage() );
162 
163       
164       long start = System.currentTimeMillis();    
165       long diff = 0;
166       while(diff < timeout) {
167         Message msg = sub.receive(timeout - diff);
168         if (msg != null) {
169           String name = (String) msg.getObjectProperty(RUNNER_NAME);
170           logger.debug("----> Found runner: " + name);
171           if (name != null && !runners.contains(name)) {
172             runners.add(name);
173           }
174         }
175         diff = System.currentTimeMillis() - start;
176       }
177     } finally {
178       if (sub != null) sub.close();
179       if (pub != null) pub.close();
180     }
181     
182     String [] retval = new String[runners.size()];
183     runners.toArray(retval);
184     
185     logger.exit("getAvailableRunners", retval);
186     return retval;
187   }
188   
189   /**
190    * Used by the AgentRunner to set the Agent's TopicConnection after it is
191    * deserialized.
192    */
193   public void setConnection(TopicConnection connx)
194   {
195     logger.entry("setConnection", connx);
196 
197     try {
198       this.connx   = connx;
199       agentSession = connx.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
200       agentTopic   = agentSession.createTopic(AGENT_TOPIC);
201     } catch (JMSException jmsex) {
202       logger.exception(jmsex);
203     }
204 
205     logger.exit("setConnection");
206   }
207 
208   /**
209    * Implement this method if you want your agent to do special processing
210    * in response to a start broadcast.   If you want your processing to 
211    * start as soon as the agent is moved simply perform all work when the
212    * run() method is called.
213    * 
214    * <note>Do not do much processing in the thread calling this method.
215    * instead a spin lock should be used to wake up thre Thread executing
216    * the run method.
217    * </note>
218    */
219   public abstract void startAgent();
220 
221   /**
222    * Implement this method if you want your agent to do something when
223    * a stop is broadcast.
224    *
225    * <note>Implementations should cause the Thread executing your run() 
226    * implementation to finish and return from the method gracefully.
227    * </note>
228    */
229   public abstract void stopAgent();
230 
231 
232   public abstract void runAgent();
233   
234   public final void run()
235   {
236     logger.entry("run");
237 
238     runAgent();
239 
240     if (agentSession != null) {
241       try {
242         agentSession.close();
243       } catch (JMSException jmsex) {
244         logger.exception(jmsex);
245       }
246     }
247 
248     logger.exit("run");
249   }
250     ///////////////////////////////////////////////////////////////////////////
251    // Package Methods                                                       //
252   ///////////////////////////////////////////////////////////////////////////
253  // None // 
254 //////////
255 
256     ///////////////////////////////////////////////////////////////////////////
257    // Protected Methods                                                     //
258   ///////////////////////////////////////////////////////////////////////////
259   
260   /**
261    * Encapsulates the creation of a message to move the Agent instance.
262    *
263    * @param runnerTargets All targets for which the Agent is destined.
264    *
265    * @return A JmsMessage containing a serialized instance of <code>this</code>
266    *         Agent instance, and all necessary message properties to ensure
267    *         the message is correctly received by all <code>AgentRunners</code>
268    */
269   protected Message createRunnerMoveMessage(String [] runnerTargets) 
270           throws JMSException
271   {
272     logger.entry("createRunnerMoveMessage", runnerTargets);
273 
274     StringBuffer buf = new StringBuffer();
275     for (int i=0; i < runnerTargets.length; ++i) {
276       buf.append(runnerTargets[i]);
277       if (i < (runnerTargets.length-1)) buf.append(';');
278     }
279       
280     ObjectMessage msg = agentSession.createObjectMessage(this); 
281     msg.setIntProperty(MESSAGE_TYPE, AGENT_MOVE);
282     msg.setStringProperty(RUNNER_PROP, buf.toString());
283 
284     logger.exit("createRunnerMoveMessage", msg);
285     return msg;
286   }
287   
288   
289   /**
290    * Encapsulates the creation of a message sent to request all runners to
291    * send their names.
292    */
293   protected Message createQueryMessage() throws JMSException
294   {
295     logger.entry("createQueryMessage");
296 
297     Message msg = agentSession.createMessage();
298     msg.setIntProperty(MESSAGE_TYPE, RUNNER_QUERY);
299 
300     logger.exit("createQueryMessage", msg);
301     return msg;
302   }
303 
304   
305   /**
306    * Encapsulates creating a subscriber with the necessary filters to 
307    * ensure retrieval of all responses to the query.
308    */
309   protected TopicSubscriber createQueryResponseReceiver() throws JMSException
310   {
311     logger.entry("createQueryResponseReceiver");
312     
313     StringBuffer filter = new StringBuffer();
314     filter.append(MESSAGE_TYPE);
315     filter.append("=");
316     filter.append(QUERY_RESPONSE);
317     
318     TopicSubscriber sub =
319       agentSession.createSubscriber(agentTopic, filter.toString(), false);
320 
321     logger.exit("createqueryResponseReceiver", sub);
322     return sub;
323   }
324   
325 
326 
327   ////////////////////////////// Misc  stuff ////////////////////////////////
328 
329   private static Logger logger = LoggerFactory.getLogger(Agent.class, null);
330 
331   ///////////////////////////////////////////////////////////////////////////
332   
333 }