Demonstrates how to start and stop the Local Server and start and stop a local map, feature, and geoprocessing service running on the Local Server.
Use case
For executing offline geoprocessing tasks in your ArcGIS Runtime apps via an offline (local) server.
How to use the sample
Choose an option from the dropdown control to filter packages by service type. Then click the Open button to choose a package. Finally, click the Start button to start the service. The service's status will be displayed in the center log.
To stop a service, select it from the Running Services list and click the Stop Service button. To go to the service's URL in your default web browser, select it and click the Go to URL button.
How it works
To start a LocalServer
and attach a LocalService
:
- Create and run a local server with
LocalServer.INSTANCE
. - Start the server asynchronously with
Server.startAsync()
. - Wait for server to be in the
LocalServerStatus.STARTED
state.- Callbacks attached to
Server.addStatusChangedListener()
will invoke whenever the status of the local server has changed.
- Callbacks attached to
- Create and run a local service. Here is an example of running a
LocalMapService
:- Instantiate
LocalMapService(Url)
to create a local map service with the given URL path to map package (mpk
ormpkx
file). - Start the job with
LocalMapService.startAsync()
.- The service is added to the
LocalServer
automatically.
- The service is added to the
- Instantiate
To stop a LocalServer
and any attached LocalServices
:
- If required, you can retrieve a list of all running services with
LocalServer.INSTANCE.getServices()
. - Stop the services asynchronously using
LocalService.stopAsync()
. - Use
LocalServer.INSTANCE.stopAsync()
to stop the server asynchronously. You can use.addDoneListener()
on this process to perform additional actions after the server is successfully stopped, such as removing temporary files.
Relevant API
- LocalFeatureService
- LocalGeoprocessingService
- LocalMapService
- LocalServer
- LocalServerStatus
- LocalService
Additional information
Local Server can be downloaded for Windows and Linux platforms from your ArcGIS Developers dashboard. Local Server is not supported on macOS.
Specific versions of ArcGIS Runtime Local Server are compatible with the version of ArcGIS Pro you use to create geoprocessing and map packages. For example, the ArcGIS Runtime API for Java v100.11.0 is configured for Local Server v100.10.0 which provides compatibility for packages created with ArcGIS Pro 2.7.x. For more information see the ArcGIS Developers guide.
To configure the ArcGIS Runtime API for Java v100.11.0 to work with Local Server 100.9.0:
- Development machine:
- Locate the Local Server installation directory and rename the folder from
LocalServer100.9
toLocalServer100.10
. - Update the environment variable from
RUNTIME_LOCAL_SERVER_100_9
toRUNTIME_LOCAL_SERVER_100_10
.
- Locate the Local Server installation directory and rename the folder from
- Deployment machine(s): Rename the deployment folder included with your application (or referenced by the LocalServerEnvironment.InstallPath property) from
LocalServer100.9
toLocalServer100.10
.
Tags
feature, geoprocessing, local services, map, server, service
Sample Code
/*
* Copyright 2017 Esri.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.esri.samples.local_server_services;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import javafx.application.HostServices;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;
import org.apache.commons.io.FileUtils;
import com.esri.arcgisruntime.localserver.LocalFeatureService;
import com.esri.arcgisruntime.localserver.LocalGeoprocessingService;
import com.esri.arcgisruntime.localserver.LocalMapService;
import com.esri.arcgisruntime.localserver.LocalServer;
import com.esri.arcgisruntime.localserver.LocalServerStatus;
import com.esri.arcgisruntime.localserver.LocalService;
public class LocalServerServicesController {
@FXML private TextField packagePath;
@FXML private ComboBox<String> serviceOptions;
@FXML private Button startServiceButton;
@FXML private TextArea statusLog;
@FXML private ListView<LocalService> runningServices;
@FXML private Button stopServiceButton;
private HostServices hostServices;
private FileChooser packageChooser;
private Path appDataPath;
@FXML
private void initialize() {
if (LocalServer.INSTANCE.checkInstallValid()) {
LocalServer server = LocalServer.INSTANCE;
// configure app data path (path length must be short for some services)
appDataPath = Path.of(System.getProperty("user.home"), "EsriSamples");
if (!appDataPath.toFile().exists()) {
try {
Files.createDirectory(appDataPath);
} catch (IOException ex) {
new Alert(AlertType.ERROR, "Failed to set local server app data path. Some processes may not work.").show();
}
}
LocalServer.INSTANCE.setAppDataPath(appDataPath.toFile().getAbsolutePath());
// log the server status
server.addStatusChangedListener(status -> statusLog.appendText("Server Status: " + status.getNewStatus()
.toString() + "\n"));
// start the local server
server.startAsync();
} else {
Platform.runLater(() -> {
Alert dialog = new Alert(AlertType.INFORMATION);
dialog.initOwner(stopServiceButton.getScene().getWindow());
dialog.setHeaderText("Local Server Load Error");
dialog.setContentText("Local Server install path couldn't be located.");
dialog.showAndWait();
Platform.exit();
});
}
// create a file chooser to select package files
packageChooser = new FileChooser();
packagePath.textProperty().bind(packageChooser.initialFileNameProperty());
packageChooser.setInitialDirectory(new File(System.getProperty("data.dir"), "./samples-data/local_server"));
packageChooser.setInitialFileName(packageChooser.getInitialDirectory().getAbsolutePath() + "/PointsOfInterest.mpkx");
// create filters to choose files for specific services
ExtensionFilter mpkFilter = new ExtensionFilter("Map Packages (*.mpk, *.mpkx)", "*.mpk", "*.mpkx");
ExtensionFilter gpkFilter = new ExtensionFilter("Geoprocessing Packages (*.gpk, *.gpkx)", "*.gpk", "*.gpkx");
packageChooser.getExtensionFilters().add(mpkFilter);
// use the ComboBox to select a filter
serviceOptions.getSelectionModel().selectedItemProperty().addListener(o -> {
packageChooser.setInitialFileName(null);
packageChooser.getExtensionFilters().clear();
if ("Geoprocessing Service".equals(serviceOptions.getSelectionModel().getSelectedItem())) {
packageChooser.getExtensionFilters().add(gpkFilter);
} else {
packageChooser.getExtensionFilters().add(mpkFilter);
}
});
// create list view representation of running services
runningServices.setCellFactory(list -> new ListCell<>() {
@Override
protected void updateItem(LocalService service, boolean bln) {
super.updateItem(service, bln);
if (service != null) {
setText(service.getName() + " : " + service.getUrl());
} else {
setText(null);
}
}
});
// setup UI bindings
stopServiceButton.disableProperty().bind(runningServices.getSelectionModel().selectedItemProperty().isNull());
startServiceButton.disableProperty().bind(packageChooser.initialFileNameProperty().isNull());
}
/**
* Creates and starts the selected service.
*/
@FXML
private void startSelectedService() {
String selected = serviceOptions.getSelectionModel().getSelectedItem();
// create local service
final LocalService localService;
final String serviceUrl = packageChooser.getInitialFileName();
switch (selected) {
case "Map Service":
localService = new LocalMapService(serviceUrl);
break;
case "Feature Service":
localService = new LocalFeatureService(serviceUrl);
break;
case "Geoprocessing Service":
localService = new LocalGeoprocessingService(serviceUrl);
break;
default:
localService = null;
}
// start local service and watch for updates
if (localService != null) {
localService.addStatusChangedListener(status -> {
statusLog.appendText(selected + " Status: " + status.getNewStatus().toString() + "\n");
if (status.getNewStatus() == LocalServerStatus.STARTED) {
Platform.runLater(() -> runningServices.getItems().add(localService));
}
});
localService.startAsync();
}
}
/**
* Opens a dialog to choose a package file.
*/
@FXML
private void openPackage() {
File selectedFile = packageChooser.showOpenDialog(packagePath.getScene().getWindow());
if (selectedFile != null) {
packageChooser.setInitialDirectory(selectedFile.getParentFile());
packageChooser.setInitialFileName(selectedFile.getAbsolutePath());
}
}
/**
* Stops the selected service from the running services list.
*/
@FXML
private void stopSelectedService() {
LocalService selectedService = runningServices.getSelectionModel().getSelectedItem();
selectedService.stopAsync();
runningServices.getItems().remove(selectedService);
}
/**
* Opens a browser to the url of the selected service.
*/
@FXML
private void openServiceURL() {
String url = runningServices.getSelectionModel().getSelectedItem().getUrl();
hostServices.showDocument(url);
}
/**
* Allows access to the Host Services of the main JavaFX application.
*
* @param hostServices Hosted Services from main JavaFX application
*/
void setHostServices(HostServices hostServices) {
this.hostServices = hostServices;
}
/**
* Stops and releases all resources used in application.
*/
void terminate() {
// make sure all services are stopped before deleting app data
LocalServer.INSTANCE.stopAsync().addDoneListener(() -> {
if (appDataPath != null) {
try {
// delete the app data
FileUtils.deleteDirectory(appDataPath.toFile());
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
}