KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > remoting > ServerInvokerCallbackHandler


1 /*
2  * JBoss, the OpenSource J2EE webOS
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  */

7 package org.jboss.remoting;
8
9 import org.jboss.logging.Logger;
10 import org.jboss.remoting.invocation.InternalInvocation;
11
12 import javax.management.MBeanServer JavaDoc;
13 import javax.management.MBeanServerInvocationHandler JavaDoc;
14 import javax.management.ObjectName JavaDoc;
15 import java.io.IOException JavaDoc;
16 import java.util.ArrayList JavaDoc;
17 import java.util.HashMap JavaDoc;
18 import java.util.List JavaDoc;
19 import java.util.Map JavaDoc;
20
21
22 /**
23  * Responsible for all callbacks in remoting at invoker level (on the server side).
24  *
25  * @author <a HREF="mailto:telrod@e2technologies.net">Tom Elrod</a>
26  */

27 public class ServerInvokerCallbackHandler implements InvokerCallbackHandler
28 {
29    private InvocationRequest invocation;
30    private Client callBackClient;
31    private ArrayList JavaDoc callbacks = new ArrayList JavaDoc();
32    private String JavaDoc sessionId;
33    private InvokerLocator serverLocator;
34
35    private SerializableStore callbackStore = null;
36
37    /**
38     * The map key to use when looking up any callback store that
39     * should be used. This key should be used when setting up
40     * config in the invoker.
41     */

42    public static final String JavaDoc CALLBACK_STORE_KEY = "callbackStore";
43
44    /**
45     * The map key to use when looking up the percentage of free memory
46     * available before tiggering persistence.
47     */

48    public static final String JavaDoc CALLBACK_MEM_CEILING = "callbackMemCeiling";
49
50    /**
51     * The percentage number of used memory before should persist messages.
52     * For example, if 64MB available and only 30MB free mem and memPercentCeiling
53     * is 50, then would trigger persisting of messages.
54     */

55    private double memPercentCeiling = 20; // 20% by default
56

57    private static final Logger log = Logger.getLogger(ServerInvokerCallbackHandler.class);
58
59
60    public ServerInvokerCallbackHandler(InvocationRequest invocation, InvokerLocator serverLocator, ServerInvoker owner) throws Exception JavaDoc
61    {
62       if(invocation == null)
63       {
64          throw new Exception JavaDoc("Can not construct ServerInvokerCallbackHandler with null InvocationRequest.");
65       }
66       this.invocation = invocation;
67       this.serverLocator = serverLocator;
68       init(invocation, owner);
69    }
70
71    private void init(InvocationRequest invocation, ServerInvoker owner) throws Exception JavaDoc
72    {
73       sessionId = invocation.getSessionId();
74       if(invocation.getLocator() != null)
75       {
76          callBackClient = new Client(invocation.getLocator(), invocation.getSubsystem());
77          callBackClient.connect();
78       }
79       else
80       {
81          createCallbackStore(owner, sessionId);
82       }
83
84       if(log.isDebugEnabled())
85       {
86          log.debug("Session id for callback handler is " + sessionId);
87       }
88    }
89
90
91    public void setMemPercentCeiling(Double JavaDoc ceiling)
92    {
93       if(ceiling != null)
94       {
95          memPercentCeiling = ceiling.doubleValue();
96       }
97    }
98
99    public Double JavaDoc getMemPercentCeiling()
100    {
101       return new Double JavaDoc(memPercentCeiling);
102    }
103
104    private void createCallbackStore(ServerInvoker owner, String JavaDoc sessionId) throws Exception JavaDoc
105    {
106       Map JavaDoc config = owner.getConfiguration();
107       if(config != null)
108       {
109          // should either be a fully qualified class name or a mbean object name
110
String JavaDoc storeName = (String JavaDoc) config.get(CALLBACK_STORE_KEY);
111          if(storeName != null)
112          {
113             // will first try as a MBean
114
try
115             {
116                MBeanServer JavaDoc server = owner.getMBeanServer();
117                ObjectName JavaDoc storeObjectName = new ObjectName JavaDoc(storeName);
118                if(server != null)
119                {
120                   callbackStore = (SerializableStore)
121                         MBeanServerInvocationHandler.newProxyInstance(server,
122                                                                       storeObjectName,
123                                                                       SerializableStore.class,
124                                                                       false);
125                }
126             }
127             catch(Exception JavaDoc ex)
128             {
129                log.debug("Could not create callback store from the configration value given (" + storeName + ") as an MBean.");
130                if(log.isTraceEnabled())
131                {
132                   log.trace("Error is: " + ex.getMessage(), ex);
133                }
134                callbackStore = null;
135             }
136
137             // now try by class name
138
if(callbackStore == null)
139             {
140                try
141                {
142                   Class JavaDoc storeClass = Class.forName(storeName);
143                   callbackStore = (SerializableStore) storeClass.newInstance();
144                }
145                catch(Exception JavaDoc e)
146                {
147                   log.debug("Could not create callback store from the configuration value given (" + storeName + ") as a fully qualified class name.");
148                   if(log.isTraceEnabled())
149                   {
150                      log.trace("Error is: " + e.getMessage(), e);
151                   }
152                }
153             }
154          }
155       }
156
157       // if still null, then just use default
158
if(callbackStore == null)
159       {
160          callbackStore = new NullCallbackStore();
161       }
162       else
163       {
164          // need to modify configuration to include session id for the callback client.
165
Map JavaDoc storeConfig = new HashMap JavaDoc();
166          storeConfig.putAll(owner.getConfiguration());
167
168          String JavaDoc newFilePath = null;
169
170          String JavaDoc filePath = (String JavaDoc) storeConfig.get(CallbackStore.FILE_PATH_KEY);
171          if(filePath == null)
172          {
173             newFilePath = System.getProperty("jboss.server.data.dir", "data");
174          }
175          newFilePath = newFilePath + System.getProperty("file.separator") + "remoting" +
176                        System.getProperty("file.separator") + sessionId;
177
178          storeConfig.put(CallbackStore.FILE_PATH_KEY, newFilePath);
179
180          callbackStore.setConfig(storeConfig);
181       }
182
183       callbackStore.create();
184       callbackStore.start();
185
186       configureMemCeiling(owner.getConfiguration());
187    }
188
189    private void configureMemCeiling(Map JavaDoc configuration)
190    {
191       if(configuration != null)
192       {
193          String JavaDoc ceiling = (String JavaDoc) configuration.get(CALLBACK_MEM_CEILING);
194          if(ceiling != null)
195          {
196             try
197             {
198                double newCeiling = Double.parseDouble(ceiling);
199                setMemPercentCeiling(new Double JavaDoc(newCeiling));
200             }
201             catch(NumberFormatException JavaDoc e)
202             {
203                log.warn("Found new store memory ceiling seting (" + ceiling + "), but can not convert to type double.", e);
204             }
205          }
206       }
207    }
208
209    public Client getCallbackClient()
210    {
211       return callBackClient;
212    }
213
214
215    /**
216     * Returns an id that can be used to identify this particular
217     * callback handler, which should be representative of the
218     * client invoker it will make callbacks to. Currently, this
219     * is the session id associated with the invocation request.
220     *
221     * @return
222     */

223    public static String JavaDoc getId(InvocationRequest invocation)
224    {
225       String JavaDoc sessionId = invocation.getSessionId();
226       return sessionId;
227    }
228
229    /**
230     * Returns an id that can be used to identify this particular
231     * callback handler, which should be representative of the
232     * client invoker it will make callbacks to.
233     *
234     * @return
235     */

236    public String JavaDoc getId()
237    {
238       return getId(invocation);
239    }
240
241    public List JavaDoc getCallbacks()
242    {
243       List JavaDoc callbackList = null;
244       synchronized(callbacks)
245       {
246          callbackList = (List JavaDoc) callbacks.clone();
247          callbacks.clear();
248       }
249
250       // get as many persisted callbacks as possible without over run on memory
251
List JavaDoc persistedCallbacks = null;
252       try
253       {
254          persistedCallbacks = getPersistedCallbacks();
255       }
256       catch(IOException JavaDoc e)
257       {
258          log.error("Can not get persisted callbacks.", e);
259          throw new RuntimeException JavaDoc("Error getting callbacks", e);
260       }
261       callbackList.addAll(persistedCallbacks);
262
263       return callbackList;
264    }
265
266    private List JavaDoc getPersistedCallbacks() throws IOException JavaDoc
267    {
268       List JavaDoc callbacks = new ArrayList JavaDoc();
269
270       int size = callbackStore.size();
271       for(int x = 0; x < size; x++)
272       {
273          callbacks.add(callbackStore.getNext());
274          // check the amount of mem in use as get callbacks out so
275
// don't load so many callbacks from store, that run out of memory.
276
if(isMemLow())
277          {
278             new Thread JavaDoc()
279             {
280                public void run()
281                {
282                   System.gc();
283                }
284             }.start();
285             break;
286          }
287       }
288
289       return callbacks;
290    }
291
292    public boolean isPullCallbackHandler()
293    {
294       return (callBackClient == null);
295    }
296
297    /**
298     * Will take the callback message and send back to client.
299     * If client locator is null, will store them till client polls to get them.
300     *
301     * @param callback
302     * @throws HandleCallbackException
303     */

304    public void handleCallback(InvocationRequest callback)
305          throws HandleCallbackException
306    {
307       try
308       {
309          if(callBackClient == null)
310          {
311             // need to check if shoudl persist callback instead of keeping in memory
312
if(shouldPersist())
313             {
314                try
315                {
316                   persistCallback(callback);
317                   callback = null;
318                   // try to help out with the amount of memory usuage
319
new Thread JavaDoc()
320                   {
321                      public void run()
322                      {
323                         System.gc();
324                      }
325                   }.start();
326                }
327                catch(IOException JavaDoc e)
328                {
329                   log.error("Unable to persist callback.", e);
330                   throw new HandleCallbackException("Unable to persist callback and will not be able to deliver.", e);
331                }
332             }
333             else
334             {
335                synchronized(callbacks)
336                {
337                   if(log.isDebugEnabled())
338                   {
339                      log.debug("pull callback. adding to callback list");
340                   }
341                   callbacks.add(callback);
342                }
343             }
344          }
345          else
346          {
347             try
348             {
349                // make sure connected
350
if(!callBackClient.isConnected())
351                {
352                   callBackClient.connect();
353                }
354                if(callBackClient.isConnected())
355                {
356                   if(log.isDebugEnabled())
357                   {
358                      log.debug("push callback. Calling client now.");
359                   }
360                   if(callback != null)
361                   {
362                      Map JavaDoc returnPayload = callback.getReturnPayload();
363                      if(returnPayload == null)
364                      {
365                         returnPayload = new HashMap JavaDoc();
366                      }
367                      returnPayload.put(Callback.SERVER_LOCATOR_KEY, serverLocator);
368                      callback.setReturnPayload(returnPayload);
369                   }
370                   // sending internal invocation so server invoker we are sending to
371
// will know how pass onto it's client callback handler
372
InternalInvocation internalInvocation = new InternalInvocation(InternalInvocation.HANDLECALLBACK,
373                                                                                  new Object JavaDoc[]{callback});
374                   callBackClient.setSessionId(sessionId);
375                   callBackClient.invoke(internalInvocation,
376                                         callback.getRequestPayload());
377                }
378                else
379                {
380                   log.error("Can not handle callback since can not connect to client invoker.");
381                   throw new HandleCallbackException("Can not handle callback since can not connect to client invoker.");
382                }
383             }
384             catch(Throwable JavaDoc ex)
385             {
386                log.debug("Error dispatching callback to handler.", ex);
387                throw new HandleCallbackException("Error dispatching callback to handler.", ex);
388             }
389          }
390       }
391       catch(Throwable JavaDoc thr)
392       {
393          log.error("Error handling callback.", thr);
394          throw new HandleCallbackException("Error handling callback.", thr);
395       }
396    }
397
398    private void persistCallback(InvocationRequest callback) throws IOException JavaDoc
399    {
400       callbackStore.add(callback);
401    }
402
403    /**
404     * Calculates the percentage amount of free memory compared to max memory. The calculations for this
405     * is not always acurate. The reason is that total memory used is usually less than the max allowed. Thus,
406     * the amount of free memory is relative to the total amount allocated at that point in time. It is not
407     * until the total amount of memory allocated is equal to the max it will be allowed to allocate. At this point,
408     * the amount of free memory becomes relavent. Therefore, if the memory percentage ceiling is high, it might
409     * not trigger until after free memory percentage is well below the ceiling.
410     *
411     * @return
412     */

413    private boolean shouldPersist()
414    {
415       return isMemLow();
416    }
417
418    private boolean isMemLow()
419    {
420       Runtime JavaDoc runtime = Runtime.getRuntime();
421       long max = runtime.maxMemory();
422       long total = runtime.totalMemory();
423       long free = runtime.freeMemory();
424       float percentage = 100 * free / total;
425       if(max == total && memPercentCeiling >= percentage)
426       {
427          return true;
428       }
429       else
430       {
431          return false;
432       }
433    }
434
435    /**
436     * Returns the id for this handler
437     *
438     * @return
439     */

440    public String JavaDoc toString()
441    {
442       return getClass().getName() + " - id: " + getId();
443    }
444
445    /**
446     * This method is required to be called upon removing a callback listener
447     * so can clean up resources used by the handler. In particular, should
448     * call disconnect on internal Client.
449     */

450    public void destroy()
451    {
452       if(callBackClient != null)
453       {
454          callBackClient.disconnect();
455          callBackClient = null;
456       }
457       if(callbackStore != null)
458       {
459          callbackStore.purgeFiles();
460       }
461    }
462 }
463
Popular Tags