AppsManager.java
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2013, OSRF.
00003  * Copyright (c) 2013, Yujin Robot.
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
00006  * use this file except in compliance with the License. You may obtain a copy of
00007  * the License at
00008  *
00009  * http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00013  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
00014  * License for the specific language governing permissions and limitations under
00015  * the License.
00016  */
00017 
00018 package com.github.rosjava.android_remocons.common_tools.rocon;
00019 
00020 import android.os.AsyncTask;
00021 import android.util.Log;
00022 
00023 import com.github.rosjava.android_remocons.common_tools.master.MasterId;
00024 
00025 import org.ros.address.InetAddressFactory;
00026 import org.ros.android.NodeMainExecutorService;
00027 import org.ros.exception.RosRuntimeException;
00028 import org.ros.exception.ServiceNotFoundException;
00029 import org.ros.internal.node.client.ParameterClient;
00030 import org.ros.internal.node.server.NodeIdentifier;
00031 import org.ros.namespace.GraphName;
00032 import org.ros.node.AbstractNodeMain;
00033 import org.ros.node.ConnectedNode;
00034 import org.ros.node.Node;
00035 import org.ros.node.NodeConfiguration;
00036 import org.ros.node.service.ServiceClient;
00037 import org.ros.node.service.ServiceResponseListener;
00038 
00039 import java.net.URI;
00040 import java.net.URISyntaxException;
00041 
00042 import rocon_interaction_msgs.GetInteraction;
00043 import rocon_interaction_msgs.GetInteractionRequest;
00044 import rocon_interaction_msgs.GetInteractionResponse;
00045 import rocon_interaction_msgs.GetInteractions;
00046 import rocon_interaction_msgs.GetInteractionsRequest;
00047 import rocon_interaction_msgs.GetInteractionsResponse;
00048 import rocon_interaction_msgs.RequestInteraction;
00049 import rocon_interaction_msgs.RequestInteractionRequest;
00050 import rocon_interaction_msgs.RequestInteractionResponse;
00051 
00052 import static com.github.rosjava.android_remocons.common_tools.rocon.Constants.ANDROID_PLATFORM_INFO;
00053 
00074 public class AppsManager extends AbstractNodeMain {
00075     public interface FailureHandler {
00079         void handleFailure(String reason);
00080     }
00081 
00082     public enum Action {
00083         NONE, GET_INTERACTIONS_FOR_ROLE, GET_INTERACTION_INFO, REQUEST_INTERACTION_USE
00084     };
00085 
00086     private String role;
00087     private String interactionsNamespace;
00088     private Action action = Action.NONE;
00089         private rocon_interaction_msgs.Interaction app;
00090     private int app_hash;  // need this separately since headless starts don't have any app info.
00091     private ConnectNodeThread connectThread;
00092     private ConnectedNode connectedNode;
00093     private NodeMainExecutorService nodeMainExecutorService;
00094     private FailureHandler failureCallback;
00095         private ServiceResponseListener<RequestInteractionResponse> requestServiceResponseListener;
00096         private ServiceResponseListener<GetInteractionsResponse>    getAppsServiceResponseListener;
00097     private ServiceResponseListener<GetInteractionResponse>     appInfoServiceResponseListener;
00098 
00099 
00100         public AppsManager(FailureHandler failureCallback) {
00101 
00102         this.failureCallback = failureCallback;
00103     }
00104 
00112     public void init(String interactionsNamespace) {
00113         this.interactionsNamespace = interactionsNamespace;
00114     }
00115 
00116     public void setupRequestService(ServiceResponseListener<RequestInteractionResponse> serviceResponseListener) {
00117         this.requestServiceResponseListener = serviceResponseListener;
00118     }
00119 
00120     public void setupGetInteractionsService(ServiceResponseListener<GetInteractionsResponse> serviceResponseListener) {
00121         this.getAppsServiceResponseListener = serviceResponseListener;
00122     }
00123 
00124     public void setupAppInfoService(ServiceResponseListener<GetInteractionResponse> serviceResponseListener) {
00125         this.appInfoServiceResponseListener = serviceResponseListener;
00126     }
00127 
00128     @Override
00129     protected void finalize() throws Throwable {
00130         super.finalize();
00131         shutdown();  // TODO not warrantied to be called, so user should call shutdown; finalize works at all in Android???  I think no....
00132     }
00133 
00134     public void shutdown() {
00135         if (nodeMainExecutorService != null)
00136             nodeMainExecutorService.shutdownNodeMain(this);
00137         else
00138             Log.w("AppsMng", "Shutting down an uninitialized apps manager");
00139     }
00140 
00141     public void getAppsForRole(final MasterId masterId, final String role) {
00142         this.action = Action.GET_INTERACTIONS_FOR_ROLE;
00143         this.role = role;
00144 
00145         // If this is the first action requested, we need a connected node, what must be done in a different thread
00146         // The requested action will be executed once we have a connected node (this object itself) in onStart method
00147         if (this.connectedNode == null) {
00148             Log.d("AppsMng", "First action requested (" + this.action + "). Starting node...");
00149             new ConnectNodeThread(masterId).start();
00150         }
00151         else {
00152             // But we don't need all this if we already executed an action, and so have a connected node. Anyway,
00153             // we must execute any action asynchronously to avoid the android.os.NetworkOnMainThreadException
00154             new AsyncTask<Void, Void, Void>() {
00155                 @Override
00156                 protected Void doInBackground(Void... params) {
00157                     getAppsForRole();
00158                     return null;
00159                 }
00160             }.execute();  // TODO: can we use this to incorporate a timeout to service calls?
00161         }
00162     }
00163 
00164     public void requestAppUse(final MasterId masterId, final String role, final rocon_interaction_msgs.Interaction app) {
00165         this.action = Action.REQUEST_INTERACTION_USE;
00166         this.role = role;
00167         this.app  = app;
00168         this.app_hash = app.getHash();
00169 
00170         // If this is the first action requested, we need a connected node, what must be done in a different thread
00171         // The requested action will be executed once we have a connected node (this object itself) in onStart method
00172         if (this.connectedNode == null) {
00173             Log.d("AppsMng", "First action requested (" + this.action + "). Starting node...");
00174             new ConnectNodeThread(masterId).start();
00175         }
00176         else {
00177             // But we don't need all this if we already executed an action, and so have a connected node. Anyway,
00178             // we must execute any action asynchronously to avoid the android.os.NetworkOnMainThreadException
00179             new AsyncTask<Void, Void, Void>() {
00180                 @Override
00181                 protected Void doInBackground(Void... params) {
00182                     requestAppUse();
00183                     return null;
00184                 }
00185             }.execute();
00186         }
00187     }
00188 
00197     public void getAppInfo(final MasterId masterId, final int hash) {
00198         this.action = Action.GET_INTERACTION_INFO;
00199         this.app_hash = hash;
00200 
00201         // If this is the first action requested, we need a connected node, what must be done in a different thread
00202         // The requested action will be executed once we have a connected node (this object itself) in onStart method
00203         if (this.connectedNode == null) {
00204             Log.d("AppsMng", "First action requested (" + this.action + "). Starting node...");
00205             new ConnectNodeThread(masterId).start();
00206         }
00207         else {
00208             // But we don't need all this if we already executed an action, and so have a connected node. Anyway,
00209             // we must execute any action asynchronously to avoid the android.os.NetworkOnMainThreadException
00210             new AsyncTask<Void, Void, Void>() {
00211                 @Override
00212                 protected Void doInBackground(Void... params) {
00213                     getAppInfo();
00214                     return null;
00215                 }
00216             }.execute();  // TODO: can we use this to incorporate a timeout to service calls?
00217         }
00218     }
00219 
00220     private void getAppsForRole() {
00221         // call get_roles_and_apps concert service
00222         ServiceClient<GetInteractionsRequest, GetInteractionsResponse> srvClient;
00223         String serviceName = this.interactionsNamespace + "/get_interactions";
00224 
00225         try {
00226             Log.d("AppsMng", "List apps service client created [" + serviceName + "]");
00227             srvClient = connectedNode.newServiceClient(serviceName, GetInteractions._TYPE);
00228         } catch (ServiceNotFoundException e) {
00229             Log.w("AppsMng", "List apps service not found [" + serviceName + "]");
00230             throw new RosRuntimeException(e); // TODO we should recover from this calling onFailure on listener
00231         }
00232         final GetInteractionsRequest request = srvClient.newMessage();
00233 
00234         request.getRoles().add(role);
00235         request.setUri(ANDROID_PLATFORM_INFO.getUri());
00236 
00237         srvClient.call(request, getAppsServiceResponseListener);
00238         Log.d("AppsMng", "List apps service call done [" + serviceName + "]");
00239     }
00240 
00241     private void requestAppUse() {
00242         // call request_interaction concert service
00243         ServiceClient<RequestInteractionRequest, RequestInteractionResponse> srvClient;
00244         String serviceName = this.interactionsNamespace + "/request_interaction";
00245         try {
00246             Log.d("AppsMng", "Request app service client created [" + serviceName + "]");
00247             srvClient = connectedNode.newServiceClient(serviceName, RequestInteraction._TYPE);
00248         } catch (ServiceNotFoundException e) {
00249             Log.w("AppsMng", "Request app service not found [" + serviceName + "]");
00250             throw new RosRuntimeException(e); // TODO we should recover from this calling onFailure on listener
00251         }
00252         final RequestInteractionRequest request = srvClient.newMessage();
00253 
00254         request.setHash(this.app.getHash());
00255 
00256         srvClient.call(request, requestServiceResponseListener);
00257         Log.d("AppsMng", "Request app service call done [" + serviceName + "]");
00258     }
00259 
00260     private void getAppInfo() {
00261         // call get_app concert service
00262         ServiceClient<GetInteractionRequest, GetInteractionResponse> srvClient;
00263         String serviceName = this.interactionsNamespace + "/get_interaction";
00264         try {
00265             Log.d("AppsMng", "Get app info service client created [" + serviceName + "]");
00266             srvClient = connectedNode.newServiceClient(serviceName, GetInteraction._TYPE);
00267         } catch (ServiceNotFoundException e) {
00268             Log.w("AppsMng", "Get app info not found [" + serviceName + "]");
00269             throw new RosRuntimeException(e); // TODO we should recover from this calling onFailure on listener
00270         }
00271         final GetInteractionRequest request = srvClient.newMessage();
00272         request.setHash(this.app_hash);
00273 
00274         srvClient.call(request, appInfoServiceResponseListener);
00275         Log.d("AppsMng", "Get app info service call done [" + serviceName + "]");
00276     }
00277 
00282     private class ConnectNodeThread extends Thread {
00283         private MasterId masterId;
00284 
00285         public ConnectNodeThread(MasterId masterId) {
00286             this.masterId = masterId;
00287             setDaemon(true);
00288             setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
00289                 @Override
00290                 public void uncaughtException(Thread thread, Throwable ex) {
00291                     failureCallback.handleFailure("exception: " + ex.getMessage());
00292                 }
00293             });
00294         }
00295 
00296         @Override
00297         public void run() {
00298             try {
00299                 URI concertUri = new URI(masterId.getMasterUri());
00300 
00301                 // Check if the master exists by looking for a master parameter
00302                 // getParam throws when it can't find the parameter (DJS: what does it throw?).
00303                 // Could get it to look for a hardcoded rocon parameter for extra guarantees
00304                 // (e.g. /rocon/version) however we'd still have to do some checking below
00305                 // when the info is there but interactions not.
00306                 ParameterClient paramClient = new ParameterClient(
00307                         NodeIdentifier.forNameAndUri("/concert_checker", concertUri.toString()), concertUri);
00308                 String name = (String) paramClient.getParam(GraphName.of("/rosversion")).getResult();
00309                 Log.i("Remocon", "Concert " + name + " found; retrieve additional information");
00310 
00311                 nodeMainExecutorService = new NodeMainExecutorService();
00312                 NodeConfiguration nodeConfiguration = NodeConfiguration.newPublic(
00313                         InetAddressFactory.newNonLoopback().getHostAddress(), concertUri);
00314                 nodeMainExecutorService.execute(AppsManager.this, nodeConfiguration.setNodeName("apps_manager_node"));
00315 
00316             } catch (URISyntaxException e) {
00317                 Log.w("AppsMng", "invalid concert URI [" + masterId.getMasterUri() + "][" + e.toString() + "]");
00318                 failureCallback.handleFailure("invalid concert URI");
00319             } catch (RuntimeException e) {
00320                 // thrown if concert could not be found in the getParam call (from java.net.ConnectException)
00321                 Log.w("AppsMng", "could not find concert [" + masterId.getMasterUri() + "][" + e.toString() + "]");
00322                 failureCallback.handleFailure(e.toString());
00323             } catch (Throwable e) {
00324                 Log.w("AppsMng", "exception while creating node in concert checker for URI " + masterId.getMasterUri(), e);
00325                 failureCallback.handleFailure(e.toString());
00326             }
00327         }
00328     }
00329 
00330     @Override
00331         public GraphName getDefaultNodeName() {
00332                 return null;
00333         }
00334 
00340         @Override
00341         public void onStart(final ConnectedNode connectedNode) {
00342         if (this.connectedNode != null) {
00343             Log.e("AppsMng", "App manager re-started before previous shutdown; ignoring...");
00344             return;
00345         }
00346 
00347         this.connectedNode = connectedNode;
00348 
00349         Log.d("AppsMng", "onStart() - " + action);
00350 
00351         switch (action) {
00352             case NONE:
00353                 Log.w("AppsMng", "Node started without specifying an action");
00354                 break;
00355             case REQUEST_INTERACTION_USE:
00356                 requestAppUse();
00357                 break;
00358             case GET_INTERACTIONS_FOR_ROLE:
00359                 getAppsForRole();
00360                 break;
00361             case GET_INTERACTION_INFO:
00362                 getAppInfo();
00363                 break;
00364             default:
00365                 Log.w("AppsMng", "Unrecogniced action requested: " + action);
00366         }
00367 
00368         Log.d("AppsMng", "Done");
00369         }
00370 
00371     @Override
00372     public void onShutdown(Node node) {
00373         Log.d("AppsMng", "Shutdown connected node...");
00374         super.onShutdown(node);
00375 
00376         // Required so we get reconnected the next time
00377         this.connectedNode = null;
00378         Log.d("AppsMng", "Done; shutdown apps manager node main");
00379     }
00380 
00381     @Override
00382     public void onShutdownComplete(Node node) {
00383         super.onShutdownComplete(node);
00384     }
00385 
00386     @Override
00387     public void onError(Node node, Throwable throwable) {
00388         super.onError(node, throwable);
00389 
00390         Log.e("AppsMng", node.getName().toString() + " node error: " + throwable.getMessage());
00391         failureCallback.handleFailure(node.getName().toString() + " node error: " + throwable.toString());
00392     }
00393 }


android_remocons
Author(s): Daniel Stonier, Kazuto Murase
autogenerated on Sat Jun 8 2019 19:32:24