"ArcGISRuntimeException: Cannot call this method in this context":when calling createDefaultParametersAsync() for online RouteTask

2492
5
08-15-2019 11:07 PM
RAMESHBACHIRAJU
New Contributor II

We are using ARCGIS Android SDK 100.5

We try to fetch the token using rest api 

             https://www.arcgis.com/sharing/rest/oauth2/token

and create UserCredentials object using token and referrer (unfortunately referrer cannot be nil in Android unlike IOS). 

We are assigning the credentials to an online RouteTask 

val routeTask = RouteTask(appContext, "http://route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World")
val credential = UserCredential.createFromToken(accessToken, "www.arcgis.com")
routeTask.credential = credential

We are getting exception :

"ArcGISRuntimeException: Cannot call this method in this context" whenever we try to get route parameters from routeTask.

for this method

val routeParameters = routeTask?.createDefaultParametersAsync()?.get()

Please note that we are following similar approach in IOS and it is working fine.

Any hints or suggestion on why this exception is coming?

5 Replies
MarkBaird
Esri Regular Contributor

Hi Ramesh,

I've had a play with this code and I suspect what is happening is the RouteTask is failing to load so you are going ahead and reading the default parameters on a service which isn't working.  You need to perform some more checks in your code.  I would also apply the same checks in your iOS app as it would fail in the same way.

My Kotlin skills are not perfect so I've tried to explain this in Java code, but the concept will be the same.

I have connected to the route service using the following code.  This is for demo purposes only and I'd recommend using Oauth instead for obvious reasons:

UserCredential userCredential = new UserCredential("username","password");
RouteTask routeTask = new RouteTask("http://route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World");
routeTask.setCredential(userCredential);

You should then load the route task and listen into the done loading listener:

routeTask.loadAsync();
routeTask.addDoneLoadingListener(()-> {

At this point in the code you have either loaded the service or something might have gone wrong!  You need to check the load status.

  if (routeTask.getLoadStatus() == LoadStatus.LOADED) {
    try {
      RouteParameters routeParameters = routeTask.createDefaultParametersAsync().get();
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (ExecutionException e) {
      e.printStackTrace();
    }
  } else {
    // failed to load so lets report the error
    System.out.println("error  " + routeTask.getLoadError().getCause());
  }

});

So you can see above that if you have got a successful connection the service you can read the default parameters.  If not the error will display on the console.

If you directly run the code above which obviously has a poor user credential you will see:

 --  loaded FAILED_TO_LOAD

 --  error com.esri.arcgisruntime.io.JsonEmbeddedException: Unable to generate token.

If you leave the user credential out you might see

 -- error  com.esri.arcgisruntime.io.JsonEmbeddedException: Token Required

The iOS SDK for runtime uses the same pattern for checking the loading of services.  This will help to explain it:

Loadable pattern for asynchronous resources—ArcGIS Runtime SDK for Android | ArcGIS for Developers 

0 Kudos
RAMESHBACHIRAJU
New Contributor II

Hi Mark,

Thanks for the feedback, have modified the code. Kindly note that in the code below I am passing on the token (hard coded for now generated using the API      https://www.arcgis.com/sharing/rest/oauth2/token

However - loadAsync fails with error saying unable to generate the token. We do not want to hardcode user name and password. Instead would like to use client_id, and client_secret. As this is not available in Android SDK, we are using rest api to generate the token. We are creating UserCredential object with token. Unfortunately UserCredential doesn't take empty referrer unlike iOS. What should be specified for referrer. 

// Find a route from mStart point to mEnd point.
private void findRoute() {
    String routeServiceURI = getResources().getString(R.string.routing_url);
    final RouteTask solveRouteTask = new RouteTask(getApplicationContext(), routeServiceURI);
    credential = UserCredential.createFromToken("Q88bbFWwQyx4vuo37xAArEfTjiguvI5kJhm5E4ZJlroboCSX7omF2I-kReJU1dHbDeJz0dWK5CrwO0Jp_bg2--BB_4zZ-D-7mnUcn0tYO5-oPKRbcl3s3GJDvvJnY7l_5h_zHOhHftzl17lmUnVaHw..", "ArcGIS.com");
    AuthenticationManager.setAuthenticationChallengeHandler(this);
    solveRouteTask.setCredential(credential);
    solveRouteTask.loadAsync();
    solveRouteTask.addDoneLoadingListener(() -> {
        if (solveRouteTask.getLoadStatus() == LoadStatus.LOADED) {
            final ListenableFuture<RouteParameters> routeParamsFuture = solveRouteTask.createDefaultParametersAsync();
            routeParamsFuture.addDoneListener(() -> {
                try {
                    RouteParameters routeParameters = routeParamsFuture.get();
                    List<Stop> stops = new ArrayList<>();
                    stops.add(new Stop(mStart));
                    stops.add(new Stop(mEnd));
                    routeParameters.setStops(stops);
                    final ListenableFuture<RouteResult> routeResultFuture = solveRouteTask.solveRouteAsync(routeParameters);
                    routeResultFuture.addDoneListener(() -> {
                        try {
                            RouteResult routeResult = routeResultFuture.get();
                            Route firstRoute = routeResult.getRoutes().get(0);
                            Polyline routePolyline = firstRoute.getRouteGeometry();
                            SimpleLineSymbol routeSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLUE, 4.0f);
                            Graphic routeGraphic = new Graphic(routePolyline, routeSymbol);
                            mGraphicsOverlay.getGraphics().add(routeGraphic);
                        } catch (InterruptedException | ExecutionException e) {
                            showError("Solve RouteTask failed " + e.getMessage());
                        }
                    });
                } catch (InterruptedException | ExecutionException e) {
                    showError("Cannot create RouteTask parameters " + e.getMessage());
                }
            });
        } else {
            showError("Unable to load RouteTask " + solveRouteTask.getLoadError().getCause().toString());
        }
    });
}

@Override
public AuthenticationChallengeResponse handleChallenge(AuthenticationChallenge authenticationChallenge) {
    return new AuthenticationChallengeResponse(AuthenticationChallengeResponse.Action.CONTINUE_WITH_CREDENTIAL, credential);
}

Is there a mechanism to pass token generated from Rest api to RouteTask ?

0 Kudos
MarkBaird
Esri Regular Contributor

My hardcoded user credentials in the above code sample were just to show the done loading pattern, as I explained this is not recommended in a real app.  

You shouldn't need to use the rest API directly to use oAuth.  Have you seen this samples code: Authenticate with OAuth | ArcGIS for Developers ?

Code is on git arcgis-runtime-samples-android/java/authenticate-with-oauth at master · Esri/arcgis-runtime-samples-... 

ChallengeHandlers are what we use for this.  You can use the DefaultChallengeHander as in the example linked above, or you could write your own.

0 Kudos
RAMESHBACHIRAJU
New Contributor II

We would like to use client-id and client-secret which are not supported by OAuthConfiguration in Android ArcGIS SDK and ArcGIS IOS SDK but are supported in .NET SDK and Rest Api.

Only way we can achieve this is by using RestApi to get the token and assign the token through UserCredential class and assign this credential object to RouteTask

However this is not working, LoadAsync throws an Error.

Any suggestions on how to manage this?

0 Kudos
appdev
by
New Contributor

Hi RAMESH BACHIRAJU‌.

I am having the same exact problem as you:
com.esri.arcgisruntime.io.JsonEmbeddedException: Unable to generate token.
Error Code: 22

I am sure the token is fine because i post the URL in a browser like http://layer_url/0?token=my_token and i see the layer information on the browser.

Did you get any solution for this? I would appreciate any help.

Thanks in advance.

0 Kudos