• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * $RCSfile$
3  * $Revision$
4  * $Date$
5  *
6  * Copyright 2005-2008 Jive Software.
7  *
8  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 package org.jivesoftware.smackx.commands;
22 
23 import org.jivesoftware.smack.*;
24 import org.jivesoftware.smack.filter.PacketFilter;
25 import org.jivesoftware.smack.filter.PacketTypeFilter;
26 import org.jivesoftware.smack.packet.IQ;
27 import org.jivesoftware.smack.packet.Packet;
28 import org.jivesoftware.smack.packet.PacketExtension;
29 import org.jivesoftware.smack.packet.XMPPError;
30 import org.jivesoftware.smack.util.StringUtils;
31 import org.jivesoftware.smackx.Form;
32 import org.jivesoftware.smackx.NodeInformationProvider;
33 import org.jivesoftware.smackx.ServiceDiscoveryManager;
34 import org.jivesoftware.smackx.commands.AdHocCommand.Action;
35 import org.jivesoftware.smackx.commands.AdHocCommand.Status;
36 import org.jivesoftware.smackx.packet.AdHocCommandData;
37 import org.jivesoftware.smackx.packet.DiscoverInfo;
38 import org.jivesoftware.smackx.packet.DiscoverInfo.Identity;
39 import org.jivesoftware.smackx.packet.DiscoverItems;
40 
41 import java.util.ArrayList;
42 import java.util.Collection;
43 import java.util.Collections;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.WeakHashMap;
47 import java.util.concurrent.ConcurrentHashMap;
48 
49 /**
50  * An AdHocCommandManager is responsible for keeping the list of available
51  * commands offered by a service and for processing commands requests.
52  *
53  * Pass in a Connection instance to
54  * {@link #getAddHocCommandsManager(org.jivesoftware.smack.Connection)} in order to
55  * get an instance of this class.
56  *
57  * @author Gabriel Guardincerri
58  */
59 public class AdHocCommandManager {
60 
61     private static final String DISCO_NAMESPACE = "http://jabber.org/protocol/commands";
62 
63     private static final String discoNode = DISCO_NAMESPACE;
64 
65     /**
66      * The session time out in seconds.
67      */
68     private static final int SESSION_TIMEOUT = 2 * 60;
69 
70     /**
71      * Map a Connection with it AdHocCommandManager. This map have a key-value
72      * pair for every active connection.
73      */
74     private static Map<Connection, AdHocCommandManager> instances =
75             new ConcurrentHashMap<Connection, AdHocCommandManager>();
76 
77     /**
78      * Register the listener for all the connection creations. When a new
79      * connection is created a new AdHocCommandManager is also created and
80      * related to that connection.
81      */
82     static {
Connection.addConnectionCreationListener(new ConnectionCreationListener() { public void connectionCreated(Connection connection) { new AdHocCommandManager(connection); } })83         Connection.addConnectionCreationListener(new ConnectionCreationListener() {
84             public void connectionCreated(Connection connection) {
85                 new AdHocCommandManager(connection);
86             }
87         });
88     }
89 
90     /**
91      * Returns the <code>AdHocCommandManager</code> related to the
92      * <code>connection</code>.
93      *
94      * @param connection the XMPP connection.
95      * @return the AdHocCommandManager associated with the connection.
96      */
getAddHocCommandsManager(Connection connection)97     public static AdHocCommandManager getAddHocCommandsManager(Connection connection) {
98         return instances.get(connection);
99     }
100 
101     /**
102      * Thread that reaps stale sessions.
103      */
104     private Thread sessionsSweeper;
105 
106     /**
107      * The Connection that this instances of AdHocCommandManager manages
108      */
109     private Connection connection;
110 
111     /**
112      * Map a command node with its AdHocCommandInfo. Note: Key=command node,
113      * Value=command. Command node matches the node attribute sent by command
114      * requesters.
115      */
116     private Map<String, AdHocCommandInfo> commands = Collections
117             .synchronizedMap(new WeakHashMap<String, AdHocCommandInfo>());
118 
119     /**
120      * Map a command session ID with the instance LocalCommand. The LocalCommand
121      * is the an objects that has all the information of the current state of
122      * the command execution. Note: Key=session ID, Value=LocalCommand. Session
123      * ID matches the sessionid attribute sent by command responders.
124      */
125     private Map<String, LocalCommand> executingCommands = new ConcurrentHashMap<String, LocalCommand>();
126 
AdHocCommandManager(Connection connection)127     private AdHocCommandManager(Connection connection) {
128         super();
129         this.connection = connection;
130         init();
131     }
132 
133     /**
134      * Registers a new command with this command manager, which is related to a
135      * connection. The <tt>node</tt> is an unique identifier of that command for
136      * the connection related to this command manager. The <tt>name</tt> is the
137      * human readable name of the command. The <tt>class</tt> is the class of
138      * the command, which must extend {@link LocalCommand} and have a default
139      * constructor.
140      *
141      * @param node the unique identifier of the command.
142      * @param name the human readable name of the command.
143      * @param clazz the class of the command, which must extend {@link LocalCommand}.
144      */
registerCommand(String node, String name, final Class<? extends LocalCommand> clazz)145     public void registerCommand(String node, String name, final Class<? extends LocalCommand> clazz) {
146         registerCommand(node, name, new LocalCommandFactory() {
147             public LocalCommand getInstance() throws InstantiationException, IllegalAccessException  {
148                 return clazz.newInstance();
149             }
150         });
151     }
152 
153     /**
154      * Registers a new command with this command manager, which is related to a
155      * connection. The <tt>node</tt> is an unique identifier of that
156      * command for the connection related to this command manager. The <tt>name</tt>
157      * is the human readeale name of the command. The <tt>factory</tt> generates
158      * new instances of the command.
159      *
160      * @param node the unique identifier of the command.
161      * @param name the human readable name of the command.
162      * @param factory a factory to create new instances of the command.
163      */
registerCommand(String node, final String name, LocalCommandFactory factory)164     public void registerCommand(String node, final String name, LocalCommandFactory factory) {
165         AdHocCommandInfo commandInfo = new AdHocCommandInfo(node, name, connection.getUser(), factory);
166 
167         commands.put(node, commandInfo);
168         // Set the NodeInformationProvider that will provide information about
169         // the added command
170         ServiceDiscoveryManager.getInstanceFor(connection).setNodeInformationProvider(node,
171                 new NodeInformationProvider() {
172                     public List<DiscoverItems.Item> getNodeItems() {
173                         return null;
174                     }
175 
176                     public List<String> getNodeFeatures() {
177                         List<String> answer = new ArrayList<String>();
178                         answer.add(DISCO_NAMESPACE);
179                         // TODO: check if this service is provided by the
180                         // TODO: current connection.
181                         answer.add("jabber:x:data");
182                         return answer;
183                     }
184 
185                     public List<DiscoverInfo.Identity> getNodeIdentities() {
186                         List<DiscoverInfo.Identity> answer = new ArrayList<DiscoverInfo.Identity>();
187                         DiscoverInfo.Identity identity = new DiscoverInfo.Identity(
188                                 "automation", name, "command-node");
189                         answer.add(identity);
190                         return answer;
191                     }
192 
193                     @Override
194                     public List<PacketExtension> getNodePacketExtensions() {
195                         return null;
196                     }
197 
198                 });
199     }
200 
201     /**
202      * Discover the commands of an specific JID. The <code>jid</code> is a
203      * full JID.
204      *
205      * @param jid the full JID to retrieve the commands for.
206      * @return the discovered items.
207      * @throws XMPPException if the operation failed for some reason.
208      */
discoverCommands(String jid)209     public DiscoverItems discoverCommands(String jid) throws XMPPException {
210         ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager
211                 .getInstanceFor(connection);
212         return serviceDiscoveryManager.discoverItems(jid, discoNode);
213     }
214 
215     /**
216      * Publish the commands to an specific JID.
217      *
218      * @param jid the full JID to publish the commands to.
219      * @throws XMPPException if the operation failed for some reason.
220      */
publishCommands(String jid)221     public void publishCommands(String jid) throws XMPPException {
222         ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager
223                 .getInstanceFor(connection);
224 
225         // Collects the commands to publish as items
226         DiscoverItems discoverItems = new DiscoverItems();
227         Collection<AdHocCommandInfo> xCommandsList = getRegisteredCommands();
228 
229         for (AdHocCommandInfo info : xCommandsList) {
230             DiscoverItems.Item item = new DiscoverItems.Item(info.getOwnerJID());
231             item.setName(info.getName());
232             item.setNode(info.getNode());
233             discoverItems.addItem(item);
234         }
235 
236         serviceDiscoveryManager.publishItems(jid, discoNode, discoverItems);
237     }
238 
239     /**
240      * Returns a command that represents an instance of a command in a remote
241      * host. It is used to execute remote commands. The concept is similar to
242      * RMI. Every invocation on this command is equivalent to an invocation in
243      * the remote command.
244      *
245      * @param jid the full JID of the host of the remote command
246      * @param node the identifier of the command
247      * @return a local instance equivalent to the remote command.
248      */
getRemoteCommand(String jid, String node)249     public RemoteCommand getRemoteCommand(String jid, String node) {
250         return new RemoteCommand(connection, node, jid);
251     }
252 
253     /**
254      * <ul>
255      * <li>Adds listeners to the connection</li>
256      * <li>Registers the ad-hoc command feature to the ServiceDiscoveryManager</li>
257      * <li>Registers the items of the feature</li>
258      * <li>Adds packet listeners to handle execution requests</li>
259      * <li>Creates and start the session sweeper</li>
260      * </ul>
261      */
init()262     private void init() {
263         // Register the new instance and associate it with the connection
264         instances.put(connection, this);
265 
266         // Add a listener to the connection that removes the registered instance
267         // when the connection is closed
268         connection.addConnectionListener(new ConnectionListener() {
269             public void connectionClosed() {
270                 // Unregister this instance since the connection has been closed
271                 instances.remove(connection);
272             }
273 
274             public void connectionClosedOnError(Exception e) {
275                 // Unregister this instance since the connection has been closed
276                 instances.remove(connection);
277             }
278 
279             public void reconnectionSuccessful() {
280                 // Register this instance since the connection has been
281                 // reestablished
282                 instances.put(connection, AdHocCommandManager.this);
283             }
284 
285             public void reconnectingIn(int seconds) {
286                 // Nothing to do
287             }
288 
289             public void reconnectionFailed(Exception e) {
290                 // Nothing to do
291             }
292         });
293 
294         // Add the feature to the service discovery manage to show that this
295         // connection supports the AdHoc-Commands protocol.
296         // This information will be used when another client tries to
297         // discover whether this client supports AdHoc-Commands or not.
298         ServiceDiscoveryManager.getInstanceFor(connection).addFeature(
299                 DISCO_NAMESPACE);
300 
301         // Set the NodeInformationProvider that will provide information about
302         // which AdHoc-Commands are registered, whenever a disco request is
303         // received
304         ServiceDiscoveryManager.getInstanceFor(connection)
305                 .setNodeInformationProvider(discoNode,
306                         new NodeInformationProvider() {
307                             public List<DiscoverItems.Item> getNodeItems() {
308 
309                                 List<DiscoverItems.Item> answer = new ArrayList<DiscoverItems.Item>();
310                                 Collection<AdHocCommandInfo> commandsList = getRegisteredCommands();
311 
312                                 for (AdHocCommandInfo info : commandsList) {
313                                     DiscoverItems.Item item = new DiscoverItems.Item(
314                                             info.getOwnerJID());
315                                     item.setName(info.getName());
316                                     item.setNode(info.getNode());
317                                     answer.add(item);
318                                 }
319 
320                                 return answer;
321                             }
322 
323                             public List<String> getNodeFeatures() {
324                                 return null;
325                             }
326 
327                             public List<Identity> getNodeIdentities() {
328                                 return null;
329                             }
330 
331                             @Override
332                             public List<PacketExtension> getNodePacketExtensions() {
333                                 return null;
334                             }
335                         });
336 
337         // The packet listener and the filter for processing some AdHoc Commands
338         // Packets
339         PacketListener listener = new PacketListener() {
340             public void processPacket(Packet packet) {
341                 AdHocCommandData requestData = (AdHocCommandData) packet;
342                 processAdHocCommand(requestData);
343             }
344         };
345 
346         PacketFilter filter = new PacketTypeFilter(AdHocCommandData.class);
347         connection.addPacketListener(listener, filter);
348 
349         sessionsSweeper = null;
350     }
351 
352     /**
353      * Process the AdHoc-Command packet that request the execution of some
354      * action of a command. If this is the first request, this method checks,
355      * before executing the command, if:
356      * <ul>
357      *  <li>The requested command exists</li>
358      *  <li>The requester has permissions to execute it</li>
359      *  <li>The command has more than one stage, if so, it saves the command and
360      *      session ID for further use</li>
361      * </ul>
362      *
363      * <br>
364      * <br>
365      * If this is not the first request, this method checks, before executing
366      * the command, if:
367      * <ul>
368      *  <li>The session ID of the request was stored</li>
369      *  <li>The session life do not exceed the time out</li>
370      *  <li>The action to execute is one of the available actions</li>
371      * </ul>
372      *
373      * @param requestData
374      *            the packet to process.
375      */
processAdHocCommand(AdHocCommandData requestData)376     private void processAdHocCommand(AdHocCommandData requestData) {
377         // Only process requests of type SET
378         if (requestData.getType() != IQ.Type.SET) {
379             return;
380         }
381 
382         // Creates the response with the corresponding data
383         AdHocCommandData response = new AdHocCommandData();
384         response.setTo(requestData.getFrom());
385         response.setPacketID(requestData.getPacketID());
386         response.setNode(requestData.getNode());
387         response.setId(requestData.getTo());
388 
389         String sessionId = requestData.getSessionID();
390         String commandNode = requestData.getNode();
391 
392         if (sessionId == null) {
393             // A new execution request has been received. Check that the
394             // command exists
395             if (!commands.containsKey(commandNode)) {
396                 // Requested command does not exist so return
397                 // item_not_found error.
398                 respondError(response, XMPPError.Condition.item_not_found);
399                 return;
400             }
401 
402             // Create new session ID
403             sessionId = StringUtils.randomString(15);
404 
405             try {
406                 // Create a new instance of the command with the
407                 // corresponding sessioid
408                 LocalCommand command = newInstanceOfCmd(commandNode, sessionId);
409 
410                 response.setType(IQ.Type.RESULT);
411                 command.setData(response);
412 
413                 // Check that the requester has enough permission.
414                 // Answer forbidden error if requester permissions are not
415                 // enough to execute the requested command
416                 if (!command.hasPermission(requestData.getFrom())) {
417                     respondError(response, XMPPError.Condition.forbidden);
418                     return;
419                 }
420 
421                 Action action = requestData.getAction();
422 
423                 // If the action is unknown then respond an error.
424                 if (action != null && action.equals(Action.unknown)) {
425                     respondError(response, XMPPError.Condition.bad_request,
426                             AdHocCommand.SpecificErrorCondition.malformedAction);
427                     return;
428                 }
429 
430                 // If the action is not execute, then it is an invalid action.
431                 if (action != null && !action.equals(Action.execute)) {
432                     respondError(response, XMPPError.Condition.bad_request,
433                             AdHocCommand.SpecificErrorCondition.badAction);
434                     return;
435                 }
436 
437                 // Increase the state number, so the command knows in witch
438                 // stage it is
439                 command.incrementStage();
440                 // Executes the command
441                 command.execute();
442 
443                 if (command.isLastStage()) {
444                     // If there is only one stage then the command is completed
445                     response.setStatus(Status.completed);
446                 }
447                 else {
448                     // Else it is still executing, and is registered to be
449                     // available for the next call
450                     response.setStatus(Status.executing);
451                     executingCommands.put(sessionId, command);
452                     // See if the session reaping thread is started. If not, start it.
453                     if (sessionsSweeper == null) {
454                         sessionsSweeper = new Thread(new Runnable() {
455                             public void run() {
456                                 while (true) {
457                                     for (String sessionId : executingCommands.keySet()) {
458                                         LocalCommand command = executingCommands.get(sessionId);
459                                         // Since the command could be removed in the meanwhile
460                                         // of getting the key and getting the value - by a
461                                         // processed packet. We must check if it still in the
462                                         // map.
463                                         if (command != null) {
464                                             long creationStamp = command.getCreationDate();
465                                             // Check if the Session data has expired (default is
466                                             // 10 minutes)
467                                             // To remove it from the session list it waits for
468                                             // the double of the of time out time. This is to
469                                             // let
470                                             // the requester know why his execution request is
471                                             // not accepted. If the session is removed just
472                                             // after the time out, then whe the user request to
473                                             // continue the execution he will recieved an
474                                             // invalid session error and not a time out error.
475                                             if (System.currentTimeMillis() - creationStamp > SESSION_TIMEOUT * 1000 * 2) {
476                                                 // Remove the expired session
477                                                 executingCommands.remove(sessionId);
478                                             }
479                                         }
480                                     }
481                                     try {
482                                         Thread.sleep(1000);
483                                     }
484                                     catch (InterruptedException ie) {
485                                         // Ignore.
486                                     }
487                                 }
488                             }
489 
490                         });
491                         sessionsSweeper.setDaemon(true);
492                         sessionsSweeper.start();
493                     }
494                 }
495 
496                 // Sends the response packet
497                 connection.sendPacket(response);
498 
499             }
500             catch (XMPPException e) {
501                 // If there is an exception caused by the next, complete,
502                 // prev or cancel method, then that error is returned to the
503                 // requester.
504                 XMPPError error = e.getXMPPError();
505 
506                 // If the error type is cancel, then the execution is
507                 // canceled therefore the status must show that, and the
508                 // command be removed from the executing list.
509                 if (XMPPError.Type.CANCEL.equals(error.getType())) {
510                     response.setStatus(Status.canceled);
511                     executingCommands.remove(sessionId);
512                 }
513                 respondError(response, error);
514                 e.printStackTrace();
515             }
516         }
517         else {
518             LocalCommand command = executingCommands.get(sessionId);
519 
520             // Check that a command exists for the specified sessionID
521             // This also handles if the command was removed in the meanwhile
522             // of getting the key and the value of the map.
523             if (command == null) {
524                 respondError(response, XMPPError.Condition.bad_request,
525                         AdHocCommand.SpecificErrorCondition.badSessionid);
526                 return;
527             }
528 
529             // Check if the Session data has expired (default is 10 minutes)
530             long creationStamp = command.getCreationDate();
531             if (System.currentTimeMillis() - creationStamp > SESSION_TIMEOUT * 1000) {
532                 // Remove the expired session
533                 executingCommands.remove(sessionId);
534 
535                 // Answer a not_allowed error (session-expired)
536                 respondError(response, XMPPError.Condition.not_allowed,
537                         AdHocCommand.SpecificErrorCondition.sessionExpired);
538                 return;
539             }
540 
541             /*
542              * Since the requester could send two requests for the same
543              * executing command i.e. the same session id, all the execution of
544              * the action must be synchronized to avoid inconsistencies.
545              */
546             synchronized (command) {
547                 Action action = requestData.getAction();
548 
549                 // If the action is unknown the respond an error
550                 if (action != null && action.equals(Action.unknown)) {
551                     respondError(response, XMPPError.Condition.bad_request,
552                             AdHocCommand.SpecificErrorCondition.malformedAction);
553                     return;
554                 }
555 
556                 // If the user didn't specify an action or specify the execute
557                 // action then follow the actual default execute action
558                 if (action == null || Action.execute.equals(action)) {
559                     action = command.getExecuteAction();
560                 }
561 
562                 // Check that the specified action was previously
563                 // offered
564                 if (!command.isValidAction(action)) {
565                     respondError(response, XMPPError.Condition.bad_request,
566                             AdHocCommand.SpecificErrorCondition.badAction);
567                     return;
568                 }
569 
570                 try {
571                     // TODO: Check that all the requierd fields of the form are
572                     // TODO: filled, if not throw an exception. This will simplify the
573                     // TODO: construction of new commands
574 
575                     // Since all errors were passed, the response is now a
576                     // result
577                     response.setType(IQ.Type.RESULT);
578 
579                     // Set the new data to the command.
580                     command.setData(response);
581 
582                     if (Action.next.equals(action)) {
583                         command.incrementStage();
584                         command.next(new Form(requestData.getForm()));
585                         if (command.isLastStage()) {
586                             // If it is the last stage then the command is
587                             // completed
588                             response.setStatus(Status.completed);
589                         }
590                         else {
591                             // Otherwise it is still executing
592                             response.setStatus(Status.executing);
593                         }
594                     }
595                     else if (Action.complete.equals(action)) {
596                         command.incrementStage();
597                         command.complete(new Form(requestData.getForm()));
598                         response.setStatus(Status.completed);
599                         // Remove the completed session
600                         executingCommands.remove(sessionId);
601                     }
602                     else if (Action.prev.equals(action)) {
603                         command.decrementStage();
604                         command.prev();
605                     }
606                     else if (Action.cancel.equals(action)) {
607                         command.cancel();
608                         response.setStatus(Status.canceled);
609                         // Remove the canceled session
610                         executingCommands.remove(sessionId);
611                     }
612 
613                     connection.sendPacket(response);
614                 }
615                 catch (XMPPException e) {
616                     // If there is an exception caused by the next, complete,
617                     // prev or cancel method, then that error is returned to the
618                     // requester.
619                     XMPPError error = e.getXMPPError();
620 
621                     // If the error type is cancel, then the execution is
622                     // canceled therefore the status must show that, and the
623                     // command be removed from the executing list.
624                     if (XMPPError.Type.CANCEL.equals(error.getType())) {
625                         response.setStatus(Status.canceled);
626                         executingCommands.remove(sessionId);
627                     }
628                     respondError(response, error);
629 
630                     e.printStackTrace();
631                 }
632             }
633         }
634     }
635 
636     /**
637      * Responds an error with an specific condition.
638      *
639      * @param response the response to send.
640      * @param condition the condition of the error.
641      */
respondError(AdHocCommandData response, XMPPError.Condition condition)642     private void respondError(AdHocCommandData response,
643             XMPPError.Condition condition) {
644         respondError(response, new XMPPError(condition));
645     }
646 
647     /**
648      * Responds an error with an specific condition.
649      *
650      * @param response the response to send.
651      * @param condition the condition of the error.
652      * @param specificCondition the adhoc command error condition.
653      */
respondError(AdHocCommandData response, XMPPError.Condition condition, AdHocCommand.SpecificErrorCondition specificCondition)654     private void respondError(AdHocCommandData response, XMPPError.Condition condition,
655             AdHocCommand.SpecificErrorCondition specificCondition)
656     {
657         XMPPError error = new XMPPError(condition);
658         error.addExtension(new AdHocCommandData.SpecificError(specificCondition));
659         respondError(response, error);
660     }
661 
662     /**
663      * Responds an error with an specific error.
664      *
665      * @param response the response to send.
666      * @param error the error to send.
667      */
respondError(AdHocCommandData response, XMPPError error)668     private void respondError(AdHocCommandData response, XMPPError error) {
669         response.setType(IQ.Type.ERROR);
670         response.setError(error);
671         connection.sendPacket(response);
672     }
673 
674     /**
675      * Creates a new instance of a command to be used by a new execution request
676      *
677      * @param commandNode the command node that identifies it.
678      * @param sessionID the session id of this execution.
679      * @return the command instance to execute.
680      * @throws XMPPException if there is problem creating the new instance.
681      */
newInstanceOfCmd(String commandNode, String sessionID)682     private LocalCommand newInstanceOfCmd(String commandNode, String sessionID)
683             throws XMPPException
684     {
685         AdHocCommandInfo commandInfo = commands.get(commandNode);
686         LocalCommand command;
687         try {
688             command = (LocalCommand) commandInfo.getCommandInstance();
689             command.setSessionID(sessionID);
690             command.setName(commandInfo.getName());
691             command.setNode(commandInfo.getNode());
692         }
693         catch (InstantiationException e) {
694             e.printStackTrace();
695             throw new XMPPException(new XMPPError(
696                     XMPPError.Condition.interna_server_error));
697         }
698         catch (IllegalAccessException e) {
699             e.printStackTrace();
700             throw new XMPPException(new XMPPError(
701                     XMPPError.Condition.interna_server_error));
702         }
703         return command;
704     }
705 
706     /**
707      * Returns the registered commands of this command manager, which is related
708      * to a connection.
709      *
710      * @return the registered commands.
711      */
getRegisteredCommands()712     private Collection<AdHocCommandInfo> getRegisteredCommands() {
713         return commands.values();
714     }
715 
716     /**
717      * Stores ad-hoc command information.
718      */
719     private static class AdHocCommandInfo {
720 
721         private String node;
722         private String name;
723         private String ownerJID;
724         private LocalCommandFactory factory;
725 
AdHocCommandInfo(String node, String name, String ownerJID, LocalCommandFactory factory)726         public AdHocCommandInfo(String node, String name, String ownerJID,
727                 LocalCommandFactory factory)
728         {
729             this.node = node;
730             this.name = name;
731             this.ownerJID = ownerJID;
732             this.factory = factory;
733         }
734 
getCommandInstance()735         public LocalCommand getCommandInstance() throws InstantiationException,
736                 IllegalAccessException
737         {
738             return factory.getInstance();
739         }
740 
getName()741         public String getName() {
742             return name;
743         }
744 
getNode()745         public String getNode() {
746             return node;
747         }
748 
getOwnerJID()749         public String getOwnerJID() {
750             return ownerJID;
751         }
752     }
753 }
754