KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > nextapp > echo2 > webcontainer > ContainerSynchronizeService


1 /*
2  * This file is part of the Echo Web Application Framework (hereinafter "Echo").
3  * Copyright (C) 2002-2005 NextApp, Inc.
4  *
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * Alternatively, the contents of this file may be used under the terms of
18  * either the GNU General Public License Version 2 or later (the "GPL"), or
19  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
20  * in which case the provisions of the GPL or the LGPL are applicable instead
21  * of those above. If you wish to allow use of your version of this file only
22  * under the terms of either the GPL or the LGPL, and not to allow others to
23  * use your version of this file under the terms of the MPL, indicate your
24  * decision by deleting the provisions above and replace them with the notice
25  * and other provisions required by the GPL or the LGPL. If you do not delete
26  * the provisions above, a recipient may use your version of this file under
27  * the terms of any one of the MPL, the GPL or the LGPL.
28  */

29
30 package nextapp.echo2.webcontainer;
31
32 import java.util.HashSet JavaDoc;
33 import java.util.Set JavaDoc;
34
35 import org.w3c.dom.Document JavaDoc;
36 import org.w3c.dom.Element JavaDoc;
37
38 import nextapp.echo2.app.ApplicationInstance;
39 import nextapp.echo2.app.Command;
40 import nextapp.echo2.app.Component;
41 import nextapp.echo2.app.Window;
42 import nextapp.echo2.app.update.PropertyUpdate;
43 import nextapp.echo2.app.update.ServerComponentUpdate;
44 import nextapp.echo2.app.update.ServerUpdateManager;
45 import nextapp.echo2.app.update.UpdateManager;
46 import nextapp.echo2.webcontainer.syncpeer.WindowPeer;
47 import nextapp.echo2.webrender.Connection;
48 import nextapp.echo2.webrender.ServerMessage;
49 import nextapp.echo2.webrender.Service;
50 import nextapp.echo2.webrender.UserInstance;
51 import nextapp.echo2.webrender.WebRenderServlet;
52 import nextapp.echo2.webrender.servermessage.WindowUpdate;
53 import nextapp.echo2.webrender.service.JavaScriptService;
54 import nextapp.echo2.webrender.service.SynchronizeService;
55 import nextapp.echo2.webrender.util.DomUtil;
56
57 /**
58  * A service which synchronizes the state of the client with that of the
59  * server. Requests made to this service are in the form of "ClientMessage"
60  * XML documents which describe the users actions since the last
61  * synchronization, e.g., input typed into text fields and the action taken
62  * (e.g., a button press) which caused the server interaction.
63  * The service then communicates these changes to the server-side application,
64  * and then generates an output "ServerMessage" containing instructions to
65  * update the client-side state of the application to the updated server-side
66  * state.
67  * <p>
68  * This class is derived from the base class <code>SynchronizeService</code>
69  * of the web renderer, which handles the lower-level work.
70  */

71 public class ContainerSynchronizeService extends SynchronizeService {
72     
73     /**
74      * Service to provide supporting JavaScript library.
75      */

76     public static final Service WEB_CONTAINER_SERVICE = JavaScriptService.forResource("Echo.WebContainer",
77             "/nextapp/echo2/webcontainer/resource/js/WebContainer.js");
78
79     static {
80         WebRenderServlet.getServiceRegistry().add(WEB_CONTAINER_SERVICE);
81     }
82
83     /**
84      * A single shared instance of this stateless service.
85      */

86     public static final ContainerSynchronizeService INSTANCE = new ContainerSynchronizeService();
87
88     /**
89      * Determines if any of the <code>Component</code> object in the provided
90      * set of "potential" ancestors is in fact an ancestor of
91      * <code>component</code>.
92      *
93      * @param potentialAncestors a set containing <code>Component</code>s
94      * @param component the <code>Component</code> to evaluate
95      * @return true if any component in <code>potentialAncestors</code> is an
96      * ancestor of <code>component</code>
97      */

98     private static boolean isAncestor(Set JavaDoc potentialAncestors, Component component) {
99         component = component.getParent();
100         while (component != null) {
101             if (potentialAncestors.contains(component)) {
102                 return true;
103             }
104             component = component.getParent();
105         }
106         return false;
107     }
108
109     /**
110      * <code>ClientMessagePartProcessor</code> to process user-interface
111      * component input message parts.
112      */

113     private ClientMessagePartProcessor propertyUpdateProcessor = new ClientMessagePartProcessor() {
114         
115         /**
116          * @see nextapp.echo2.webrender.service.SynchronizeService.ClientMessagePartProcessor#getName()
117          */

118         public String JavaDoc getName() {
119             return "EchoPropertyUpdate";
120         }
121         
122         /**
123          * @see nextapp.echo2.webrender.service.SynchronizeService.ClientMessagePartProcessor#process(
124          * nextapp.echo2.webrender.UserInstance, org.w3c.dom.Element)
125          */

126         public void process(UserInstance userInstance, Element JavaDoc messagePartElement) {
127             ContainerInstance ci = (ContainerInstance) userInstance;
128             Element JavaDoc[] propertyElements = DomUtil.getChildElementsByTagName(messagePartElement, "property");
129             for (int i = 0; i < propertyElements.length; ++i) {
130                 String JavaDoc componentId = propertyElements[i].getAttribute("component-id");
131                 Component component = ci.getComponentByElementId(componentId);
132                 if (component == null) {
133                     // Component removed. This should not frequently occur, however in certain cases,
134
// e.g., dragging a window during an during before, during, after a server pushed update
135
// can result in the condition where input is received from a component which no longer
136
// is registered.
137
continue;
138                 }
139                 ComponentSynchronizePeer syncPeer = SynchronizePeerFactory.getPeerForComponent(component.getClass());
140                 if (!(syncPeer instanceof PropertyUpdateProcessor)) {
141                     throw new IllegalStateException JavaDoc("Target peer is not an PropertyUpdateProcessor.");
142                 }
143                 ((PropertyUpdateProcessor) syncPeer).processPropertyUpdate(ci, component, propertyElements[i]);
144             }
145         }
146     };
147
148     /**
149      * <code>ClientMessagePartProcessor</code> to process user-interface
150      * component action message parts.
151      */

152     private ClientMessagePartProcessor actionProcessor = new ClientMessagePartProcessor() {
153         
154         /**
155          * @see nextapp.echo2.webrender.service.SynchronizeService.ClientMessagePartProcessor#getName()
156          */

157         public String JavaDoc getName() {
158             return "EchoAction";
159         }
160         
161         /**
162          * @see nextapp.echo2.webrender.service.SynchronizeService.ClientMessagePartProcessor#process(
163          * nextapp.echo2.webrender.UserInstance, org.w3c.dom.Element)
164          */

165         public void process(UserInstance userInstance, Element JavaDoc messagePartElement) {
166             ContainerInstance ci = (ContainerInstance) userInstance;
167             Element JavaDoc actionElement = DomUtil.getChildElementByTagName(messagePartElement, "action");
168             String JavaDoc componentId = actionElement.getAttribute("component-id");
169             Component component = ci.getComponentByElementId(componentId);
170             if (component == null) {
171                 // Component removed. This should not frequently occur, however in certain cases,
172
// e.g., dragging a window during an during before, during, after a server pushed update
173
// can result in the condition where input is received from a component which no longer
174
// is registered.
175
return;
176             }
177             ComponentSynchronizePeer syncPeer = SynchronizePeerFactory.getPeerForComponent(component.getClass());
178             if (!(syncPeer instanceof ActionProcessor)) {
179                 throw new IllegalStateException JavaDoc("Target peer is not an ActionProcessor.");
180             }
181             ((ActionProcessor) syncPeer).processAction(ci, component, actionElement);
182         }
183     };
184     
185     /**
186      * Creates a new <code>ContainerSynchronizeService</code>.
187      * Installs "ClientMessage" part processors.
188      */

189     private ContainerSynchronizeService() {
190         super();
191         registerClientMessagePartProcessor(propertyUpdateProcessor);
192         registerClientMessagePartProcessor(actionProcessor);
193     }
194     
195     /**
196      * Performs disposal operations on components which have been removed from
197      * the hierarchy. Removes any <code>RenderState</code> objects being
198      * stored in the <code>ContainerInstance</code> for the disposed
199      * components. Invokes <code>ComponentSynchronizePeer.renderDispose()</code>
200      * such that the peers of the components can dispose of resources on the
201      * client.
202      *
203      * @param rc the relevant <code>RenderContext</code>
204      * @param componentUpdate the <code>ServerComponentUpdate</code> causing
205      * components to be disposed.
206      * @param disposedComponents the components to dispose
207      */

208     private void disposeComponents(RenderContext rc, ServerComponentUpdate componentUpdate,
209             Component[] disposedComponents) {
210         ContainerInstance ci = rc.getContainerInstance();
211         for (int i = 0; i < disposedComponents.length; ++i) {
212             ComponentSynchronizePeer disposedSyncPeer = SynchronizePeerFactory.getPeerForComponent(
213                     disposedComponents[i].getClass());
214             disposedSyncPeer.renderDispose(rc, componentUpdate, disposedComponents[i]);
215             ci.removeRenderState(disposedComponents[i]);
216         }
217     }
218     
219     /**
220      * Invokes <code>renderDispose()</code> on
221      * <code>ComponentSynchronizePeer</code>s in a hierarchy of Components that is
222      * be re-rendered on the client. That is, this hierarchy of components exist on
223      * the client, are being removed, and will be re-rendered due to a container
224      * component NOT being capable of rendering a partial update.
225      * This method is invoked recursively.
226      *
227      * @param rc the relevant <code>RenderContext</code>
228      * @param update the update
229      * @param parent the <code>Component</code> whose descendants should be disposed
230      */

231     private void disposeReplacedDescendants(RenderContext rc, ServerComponentUpdate update, Component parent) {
232         Component[] replacedComponents = parent.getVisibleComponents();
233         boolean isRoot = parent == update.getParent();
234         for (int i = 0; i < replacedComponents.length; ++i) {
235             // Verify that component was not added on this synchronization.
236
if (isRoot && update.hasAddedChild(replacedComponents[i])) {
237                 // Component was added as a child on this synchronization:
238
// There is no reason to dispose of it as it does not yet exist on the client.
239
continue;
240             }
241             
242             // Recursively dispose child components.
243
disposeReplacedDescendants(rc, update, replacedComponents[i]);
244             
245             // Dispose component.
246
ComponentSynchronizePeer syncPeer = SynchronizePeerFactory.getPeerForComponent(replacedComponents[i].getClass());
247             syncPeer.renderDispose(rc, update, replacedComponents[i]);
248         }
249     }
250     
251     /**
252      * Determines if the specified <code>component</code> has been rendered to
253      * the client by determining if it is a descendant of any
254      * <code>LazyRenderContainer</code>s and if so querying them to determine
255      * the hierarchy's render state. This method is recursively invoked.
256      *
257      * @param ci the relevant <code>ContainerInstance</code>
258      * @param component the <code>Component</code> to analyze
259      * @return <code>true</code> if the <code>Component</code> has been
260      * rendered to the client
261      */

262     private boolean isRendered(ContainerInstance ci, Component component) {
263         Component parent = component.getParent();
264         if (parent == null) {
265             return true;
266         }
267         ComponentSynchronizePeer syncPeer = SynchronizePeerFactory.getPeerForComponent(parent.getClass());
268         if (syncPeer instanceof LazyRenderContainer) {
269             boolean rendered = ((LazyRenderContainer) syncPeer).isRendered(ci, parent, component);
270             if (!rendered) {
271                 return false;
272             }
273         }
274         return isRendered(ci, parent);
275     }
276     
277     /**
278      * Retrieves information about the current focused component on the client,
279      * if provided, and in such case notifies the
280      * <code>ApplicationInstance</code> of the focus.
281      *
282      * @param rc the relevant <code>RenderContext</code>
283      * @param clientMessageDocument the ClientMessage <code>Document</code> to
284      * retrieve focus information from
285      */

286     private void processClientFocusedComponent(RenderContext rc, Document JavaDoc clientMessageDocument) {
287         if (clientMessageDocument.getDocumentElement().hasAttribute("focus")) {
288             String JavaDoc focusedComponentId = clientMessageDocument.getDocumentElement().getAttribute("focus");
289             Component component = null;
290             if (focusedComponentId.length() > 2) {
291                 // Valid component id.
292
component = rc.getContainerInstance().getComponentByElementId(focusedComponentId);
293             }
294             ApplicationInstance applicationInstance = rc.getContainerInstance().getApplicationInstance();
295             applicationInstance.getUpdateManager().getClientUpdateManager().setApplicationProperty(
296                     ApplicationInstance.FOCUSED_COMPONENT_CHANGED_PROPERTY, component);
297         }
298     }
299     
300     /**
301      * Handles an invalid transaction id scenario, reinitializing the entire
302      * state of the client.
303      *
304      * @param rc the relevant <code>RenderContex</code>
305      */

306     private void processInvalidTransaction(RenderContext rc) {
307         WindowUpdate.renderReload(rc.getServerMessage());
308     }
309     
310     /**
311      * Executes queued <code>Command</code>s.
312      *
313      * @param rc the relevant <code>RenderContext</code>
314      */

315     private void processQueuedCommands(RenderContext rc) {
316         ServerUpdateManager serverUpdateManager = rc.getContainerInstance().getUpdateManager().getServerUpdateManager();
317         Command[] commands = serverUpdateManager.getCommands();
318         for (int i = 0; i < commands.length; i++) {
319             CommandSynchronizePeer peer = SynchronizePeerFactory.getPeerForCommand(commands[i].getClass());
320             peer.render(rc, commands[i]);
321         }
322     }
323     
324     /**
325      * Processes updates from the application, generating an outgoing
326      * <code>ServerMessage</code>.
327      *
328      * @param rc the relevant <code>RenderContext</code>
329      */

330     private void processServerUpdates(RenderContext rc) {
331         ContainerInstance ci = rc.getContainerInstance();
332         UpdateManager updateManager = ci.getUpdateManager();
333         ServerUpdateManager serverUpdateManager = updateManager.getServerUpdateManager();
334         ServerComponentUpdate[] componentUpdates = updateManager.getServerUpdateManager().getComponentUpdates();
335         
336         if (serverUpdateManager.isFullRefreshRequired()) {
337             Window window = rc.getContainerInstance().getApplicationInstance().getDefaultWindow();
338             ServerComponentUpdate fullRefreshUpdate = componentUpdates[0];
339             
340             // Dispose of removed descendants.
341
Component[] removedDescendants = fullRefreshUpdate.getRemovedDescendants();
342             disposeComponents(rc, fullRefreshUpdate, removedDescendants);
343             
344             // Perform full refresh.
345
RootSynchronizePeer rootSyncPeer
346                     = (RootSynchronizePeer) SynchronizePeerFactory.getPeerForComponent(window.getClass());
347             rootSyncPeer.renderRefresh(rc, fullRefreshUpdate, window);
348             
349             setRootLayoutDirection(rc);
350         } else {
351             // Remove any updates whose updates are descendants of components which have not been rendered to the
352
// client yet due to lazy-loading containers.
353
for (int i = 0; i < componentUpdates.length; ++i) {
354                 if (!isRendered(ci, componentUpdates[i].getParent())) {
355                     componentUpdates[i] = null;
356                 }
357             }
358             
359             // Set of Components whose HTML was entirely re-rendered, negating the need
360
// for updates of their children to be processed.
361
Set JavaDoc fullyReplacedHierarchies = new HashSet JavaDoc();
362     
363             for (int i = 0; i < componentUpdates.length; ++i) {
364                 if (componentUpdates[i] == null) {
365                     // Update removed, do nothing.
366
continue;
367                 }
368                 
369                 // Dispose of removed children.
370
Component[] removedChildren = componentUpdates[i].getRemovedChildren();
371                 disposeComponents(rc, componentUpdates[i], removedChildren);
372
373                 // Dispose of removed descendants.
374
Component[] removedDescendants = componentUpdates[i].getRemovedDescendants();
375                 disposeComponents(rc, componentUpdates[i], removedDescendants);
376     
377                 // Perform update.
378
Component parentComponent = componentUpdates[i].getParent();
379                 if (!isAncestor(fullyReplacedHierarchies, parentComponent)) {
380                     // Only perform update if ancestor of updated component is NOT contained in
381
// the set of components whose descendants were fully replaced.
382
ComponentSynchronizePeer syncPeer = SynchronizePeerFactory.getPeerForComponent(parentComponent.getClass());
383                     String JavaDoc targetId;
384                     if (parentComponent.getParent() == null) {
385                         targetId = null;
386                     } else {
387                         ComponentSynchronizePeer parentSyncPeer
388                                 = SynchronizePeerFactory.getPeerForComponent(parentComponent.getParent().getClass());
389                         targetId = parentSyncPeer.getContainerId(parentComponent);
390                     }
391                     boolean fullReplacement = syncPeer.renderUpdate(rc, componentUpdates[i], targetId);
392                     if (fullReplacement) {
393                         // Invoke renderDispose() on hierarchy of components destroyed by
394
// the complete replacement.
395
disposeReplacedDescendants(rc, componentUpdates[i], parentComponent);
396                         fullyReplacedHierarchies.add(parentComponent);
397                     }
398                 }
399             }
400         }
401     }
402     
403     /**
404      * @see nextapp.echo2.webrender.service.SynchronizeService#renderInit(nextapp.echo2.webrender.Connection,
405      * org.w3c.dom.Document)
406      */

407     protected ServerMessage renderInit(Connection conn, Document JavaDoc clientMessageDocument) {
408         ServerMessage serverMessage = new ServerMessage();
409         RenderContext rc = new RenderContextImpl(conn, serverMessage);
410         ContainerInstance containerInstance = rc.getContainerInstance();
411         try {
412             serverMessage.addLibrary(WEB_CONTAINER_SERVICE.getId());
413             
414             processClientMessage(conn, clientMessageDocument);
415
416             if (!containerInstance.isInitialized()) {
417                 containerInstance.init(conn);
418             }
419             
420             ApplicationInstance applicationInstance = rc.getContainerInstance().getApplicationInstance();
421             ApplicationInstance.setActive(applicationInstance);
422
423             Window window = applicationInstance.getDefaultWindow();
424             
425             ServerComponentUpdate componentUpdate = new ServerComponentUpdate(window);
426             ComponentSynchronizePeer syncPeer = SynchronizePeerFactory.getPeerForComponent(window.getClass());
427             ((WindowPeer) syncPeer).renderRefresh(rc, componentUpdate, window);
428             
429             //TODO. clean-up how these operations are invoked on init/update.
430
setAsynchronousMonitorInterval(rc);
431             setFocus(rc, true);
432             setModalContextRootId(rc);
433             setRootLayoutDirection(rc);
434
435             processQueuedCommands(rc);
436             
437             applicationInstance.getUpdateManager().purge();
438             
439             return serverMessage;
440         } finally {
441             ApplicationInstance.setActive(null);
442         }
443     }
444     
445     /**
446      * @see nextapp.echo2.webrender.service.SynchronizeService#renderUpdate(nextapp.echo2.webrender.Connection,
447      * org.w3c.dom.Document)
448      */

449     protected ServerMessage renderUpdate(Connection conn, Document JavaDoc clientMessageDocument) {
450         ServerMessage serverMessage = new ServerMessage();
451         RenderContext rc = new RenderContextImpl(conn, serverMessage);
452         
453         ContainerInstance ci = rc.getContainerInstance();
454         ApplicationInstance applicationInstance = ci.getApplicationInstance();
455         
456         try {
457             if (!validateTransactionId(ci, clientMessageDocument)) {
458                 processInvalidTransaction(rc);
459                 return serverMessage;
460             }
461             
462             // Mark instance as active.
463
ApplicationInstance.setActive(applicationInstance);
464             
465             UpdateManager updateManager = applicationInstance.getUpdateManager();
466             
467             processClientFocusedComponent(rc, clientMessageDocument);
468             
469             // Process updates from client.
470
processClientMessage(conn, clientMessageDocument);
471             
472             updateManager.processClientUpdates();
473             
474             // Process updates from server.
475
processServerUpdates(rc);
476             
477             setAsynchronousMonitorInterval(rc);
478             setFocus(rc, false);
479             setModalContextRootId(rc);
480             
481             processQueuedCommands(rc);
482
483             updateManager.purge();
484             
485             return serverMessage;
486         } finally {
487             // Mark instance as inactive.
488
ApplicationInstance.setActive(null);
489         }
490     }
491
492     /**
493      * Sets the interval between asynchronous monitor requests.
494      *
495      * @param rc the relevant <code>RenderContext</code>.
496      */

497     private void setAsynchronousMonitorInterval(RenderContext rc) {
498         boolean hasTaskQueues = rc.getContainerInstance().getApplicationInstance().hasTaskQueues();
499         if (hasTaskQueues) {
500             int interval = rc.getContainerInstance().getCallbackInterval();
501             rc.getServerMessage().setAsynchronousMonitorInterval(interval);
502         } else {
503             rc.getServerMessage().setAsynchronousMonitorInterval(-1);
504         }
505     }
506     
507     /**
508      * Update the <code>ServerMessage</code> to set the focused component if
509      * required.
510      *
511      * @param rc the relevant <code>RenderContext</code>
512      * @param initial a flag indicating whether the initial synchronization is
513      * being performed, i.e., whether this method is being invoked from
514      * <code>renderInit()</code>
515      */

516     private void setFocus(RenderContext rc, boolean initial) {
517         ApplicationInstance applicationInstance = rc.getContainerInstance().getApplicationInstance();
518         Component focusedComponent = null;
519         if (initial) {
520             focusedComponent = applicationInstance.getFocusedComponent();
521         } else {
522             ServerUpdateManager serverUpdateManager = applicationInstance.getUpdateManager().getServerUpdateManager();
523             PropertyUpdate focusUpdate =
524                     serverUpdateManager.getApplicationPropertyUpdate(ApplicationInstance.FOCUSED_COMPONENT_CHANGED_PROPERTY);
525             if (focusUpdate != null) {
526                 focusedComponent = (Component) focusUpdate.getNewValue();
527             }
528         }
529
530         if (focusedComponent != null) {
531             ComponentSynchronizePeer componentSyncPeer
532                     = SynchronizePeerFactory.getPeerForComponent(focusedComponent.getClass());
533             if (componentSyncPeer instanceof FocusSupport) {
534                 ((FocusSupport) componentSyncPeer).renderSetFocus(rc, focusedComponent);
535             }
536         }
537     }
538     
539     /**
540      * Update the <code>ServerMessage</code> to describe the current root
541      * element of the modal context.
542      *
543      * @param rc the relevant <code>RenderContext</code>
544      */

545     private void setModalContextRootId(RenderContext rc) {
546         ApplicationInstance applicationInstance = rc.getContainerInstance().getApplicationInstance();
547         Component modalContextRoot = applicationInstance.getModalContextRoot();
548         if (modalContextRoot == null) {
549             rc.getServerMessage().setModalContextRootId(null);
550         } else {
551             rc.getServerMessage().setModalContextRootId(ContainerInstance.getElementId(modalContextRoot));
552         }
553     }
554     
555     /**
556      * Update the <code>ServerMessage</code> to describe the current root
557      * layout direction
558      *
559      * @param rc the relevant <code>RenderContext</code>
560      */

561     private void setRootLayoutDirection(RenderContext rc) {
562         ApplicationInstance applicationInstance = rc.getContainerInstance().getApplicationInstance();
563         rc.getServerMessage().setRootLayoutDirection(applicationInstance.getLayoutDirection().isLeftToRight()
564                 ? ServerMessage.LEFT_TO_RIGHT : ServerMessage.RIGHT_TO_LEFT);
565     }
566     
567     /**
568      * Determines if transaction id retrieved from client matches current transaction id.
569      *
570      * @param containerInstance the relevant <code>ContainerInstance</code>
571      * @param clientMessageDocument the incoming client message
572      * @return true if the transaction id is valid
573      */

574     private boolean validateTransactionId(ContainerInstance containerInstance, Document JavaDoc clientMessageDocument) {
575         try {
576             long clientTransactionId = Long.parseLong(clientMessageDocument.getDocumentElement().getAttribute("trans-id"));
577             return containerInstance.getCurrentTransactionId() == clientTransactionId;
578         } catch (NumberFormatException JavaDoc ex) {
579             // Client has not provided a transaction id at all, return true.
580
// This should not occur.
581
return true;
582         }
583     }
584 }
585
Popular Tags