Connect to Context Service
Network Connectivity Requirements
Context Service is a public Internet service that operates on port 443 (HTTPS). Context Service connects to servers at the following URLs:
- *.webex.com
- *.wbx2.com
- *.ciscoccservice.com
Proxy Configuration
You can connect to Context Service through a proxy if your application does not have a direct connection to the internet. Context Service supports a HTTP proxy server. Configure the proxy server by setting the property contextservice.proxyURL
.
If you are upgrading from before SDK Version 2.0.1, you need to reinitialize your client to get
contextservice.proxyURL
. To reinitialize your client, callupdateAndReloadConfigAsync()
.
You can set the proxy by:
- Hard coding the proxy setting in your code when you create a client connection or a management connection:
System.setProperty("contextservice.proxyURL","http://<proxy_host>:<port_number>")
- Using a JVM argument to set the proxy when you start your application:
java -jar myApp.jar -Dcontextservice.proxyURL=http://<proxy_host>:<port_number>
This proxy configuration only applies to the SDK. Your browser may also require a proxy during registration, when your application calls the web browser, or when accessing Cisco Webex Control Hub. Browser proxy configuration is managed through the browser settings and is independent from Context Service.
Context Service Connections
After your application is registered with Cisco Webex Control Hub, your application creates two connections to the Cisco Cloud using your connection data:
- Context Service Management Connection—Reports the connection status to Cisco Webex Control Hub and automatically refreshes the internal password for your connection data.
- Context Service Client Connection—Uses the Context Service client with your application.
Both connections require you to authenticate by providing your connection data.
Typically, you create a single management connection and have one or more client connections that connect to Context Service.
Each server connected to Context Service must have a unique host name specified in
ConnectorInfoImpl
.
Cisco Webex Control Hub requires that you renew the internal password associated with the connection data every 270 days. The Management Connector is responsible for keeping the connection data valid. When the internal password for the connection data changes, the management connector fires a listener that you can use to update the connection data for all clients associated with the Management Connector's connection data.
Do not share connection data. Connection data contains sensitive information, possibly including authentication credentials. Handle connection data securely to ensure privacy and safety.
Create Connection Listeners
Listeners allow your application to listen for a connection change, then perform an action.
Create State Listeners
Use the addStateListener()
method to create a state listener before initializing the management connector and the Context Service Client connector. This allows your application to act on a connector state change.
The three connector states are coded as constants in the Context Service SDK:
ConnectorState.STOPPED
ConnectorState.REGISTERED
ConnectorState.UNREGISTERED
This example shows how to create a state listener for the management connector. The function takes managementConnector
as the input parameter and returns the created state listener.
Copy/**
* Before initializing the connector, create and add a ConnectorStateListener to the ManagementConnector.
* @param managementConnector
* @param isRegistered
* @return the created ConnectorStateListener
*/
public static ConnectorStateListener addStateListenerToManagementConnectorWithFlag(ManagementConnector managementConnector, final AtomicBoolean isRegistered){
ConnectorStateListener stateListener = new ConnectorStateListener() {
public ConnectorState connectorState;
@Override
public void stateChanged(ConnectorState previousState, ConnectorState newState)
{
connectorState = newState;
LOGGER.info("Management Connector state changed: " + newState);
if (newState == ConnectorState.STOPPED) {
// Perform optional cleanup tasks; update state related application flags
if(null!= isRegistered)
isRegistered.set(false);
LOGGER.info("Management Connector stopped.");
}else if (newState == ConnectorState.REGISTERED) {
// Perform any actions needed once connector is registered; update state related application flags
if(null!= isRegistered)
isRegistered.set(true);
LOGGER.info("Management Connector registered.");
}
}
};
managementConnector.addStateListener(stateListener);
return stateListener;
}
This example shows how to create a state listener for the Context Service client connector. The function takes contextServiceClient
as the input parameter and returns the created state listener.
Copy/**
* Before initializing the connector, create and add a ConnectorStateListener to the ContextServiceClient.
* @param contextServiceClient
* @return the created ConnectorStateListener
*/
public static CustomConnectorStateListener addStateListenerToContextConnector(ContextServiceClient contextServiceClient){
CustomConnectorStateListener stateListener = new CustomConnectorStateListener();
contextServiceClient.addStateListener(stateListener);
return stateListener;
}
Create Credentials Listeners
Use the addCredentialsChangedListener()
method to create a credentials listener before initializing the management connector. This allows your application to act on a credentials change.
This example shows how to create a credentials listener for the management connector. The function takes managementConnector
as the input parameter and returns the created credentials listener.
Copy/**
* Create and add a CredentialsChangedListener to the ManagementConnector.
* It's recommended that you do this before initializing the connector.
* @param managementConnector
* @return the created CredentialsChangedListener
*/
public static CredentialsChangedListener addCredentialsListenerToManagementConnector(ManagementConnector managementConnector, final ContextServiceClient contextServiceClient){
CredentialsChangedListener credentialsChangedListener = new CredentialsChangedListener() {
String connectionData;
@Override
public void credentialsChanged(String newConnectionData) {
LOGGER.info("ConnectionData changed: " + newConnectionData);
connectionData = newConnectionData;
// Connection data is not usually logged due to security considerations.
String hostname = "doctest.example.com";
ConnectorInfoImpl connInfo = new ConnectorInfoImpl(hostname);
ConnectorConfiguration configuration = new ConnectorConfiguration() {{
addProperty("LAB_MODE", true); // exclude this line for production mode
addProperty("REQUEST_TIMEOUT", 10000);
}};
// Notify contextServiceClient that the connection data changed.
contextServiceClient.updateAndReloadConfigAsync(connectionData, connInfo, configuration, null);
}
};
managementConnector.addCredentialsChangedListener(credentialsChangedListener);
return credentialsChangedListener;
}
Management Connection
Before you create a Context Service management connection, you must:
- Initialize the connector factory.
- Save the
connectionData
string created by Registering Your Application with Context Service.
You cannot create a management connection and a client connection on the same Java virtual machine.
Before you initialize your management connector, optionally test your connection using getStatus
. See Check Connection State for more information.
To create a Context Service management connection:
- Create and initialize a `ManagementConnector` object and call `ConnectoryFactory.getConnector()` on the `ManagementConnector.class`.
- (Optional) Define the base configuration for your management connection by creating a `ConnectorConfiguration` object and adding properties. Create a base configuration only to change the default configuration values. The default values are:
Property | Description | Default Value |
---|---|---|
LAB_MODE | Sets the mode for your organization as either lab mode (true ) or production mode (false ). |
false |
REQUEST_TIMEOUT | Sets the amount of time that the method waits before the request times out. You can timeout create , get , and search methods. |
10,000 ms |
RETRIES | Sets the number of times that the method retries sending a request on a timeout error. | 1 |
- Create a connection state listener using the `addStateListener()` method on the management connector.
- Create a listener class and add it as a `CredentialsChangeListener`. This listener class is used to update the `ConnectionData` string if the credentials need to change based on a machine password reset requirement.
- Initialize the connection using `init`. You must specify the connection data for authentication and your application's host name. The host name is used to display your applications status in Cisco Collaboration Management.
This example shows how to create and initialize the management connector while using a connection state listener and a credentials listener.
Copy/**
* Create and initialize the ManagementConnector with custom configuration.
* ConnectorFactory should already be initialized.
* @param connectionData
* @return an initialized ManagementConnector
*/
public static ManagementConnector createAndInitManagementConnectorWithCustomConfiguration(String connectionData){
AtomicBoolean isRegistered = new AtomicBoolean(false);
ManagementConnector managementConnector = ConnectorFactory.getConnector(ManagementConnector.class);
// Add Management connector state listener. It needs to be done before calling init on the connector
mgmtConnectorStateListener = addStateListenerToManagementConnectorWithFlag(managementConnector, isRegistered);
String hostname = "doctest.example.com";
ConnectorInfoImpl connInfo = new ConnectorInfoImpl(hostname);
ConnectorConfiguration configuration = new ConnectorConfiguration(){{
addProperty("LAB_MODE", true); // exclude this line for production mode
addProperty("REQUEST_TIMEOUT", 10000);
}};
managementConnector.init(connectionData, connInfo, configuration);
// Optionally, parse the JSON returned by getStatus for additional status information
String status = managementConnector.getStatus();
// Connector could be already registered in Before Class, so check if it is already registered
if(! status.contains("REGISTERED")) {
try {
waitForConnectorToRegister(isRegistered, 3);
LOGGER.info(">>>> Management Connector initialized successfully");
} catch (Exception e) {
LOGGER.error(">>>> Management Connector FAILED to initialize successfully", e);
}
}
return managementConnector;
}
/**
* Before initializing the connector, create and add a ConnectorStateListener to the ManagementConnector.
* @param managementConnector
* @param isRegistered
* @return the created ConnectorStateListener
*/
public static ConnectorStateListener addStateListenerToManagementConnectorWithFlag(ManagementConnector managementConnector, final AtomicBoolean isRegistered){
ConnectorStateListener stateListener = new ConnectorStateListener() {
public ConnectorState connectorState;
@Override
public void stateChanged(ConnectorState previousState, ConnectorState newState)
{
connectorState = newState;
LOGGER.info("Management Connector state changed: " + newState);
if (newState == ConnectorState.STOPPED) {
// Perform optional cleanup tasks; update state related application flags
if(null!= isRegistered)
isRegistered.set(false);
LOGGER.info("Management Connector stopped.");
}else if (newState == ConnectorState.REGISTERED) {
// Perform any actions needed once connector is registered; update state related application flags
if(null!= isRegistered)
isRegistered.set(true);
LOGGER.info("Management Connector registered.");
}
}
};
managementConnector.addStateListener(stateListener);
return stateListener;
}
/**
* Wait for the connector state to be REGISTERED or throw an exception if timeoutSec value is reached.
*
* This example shows how to wait for the REGISTERED state by checking a connector state application flag
* which is updated in the stateListener.
* @param isRegistered
* @param timeoutSec
* @throws Exception
*/
public static void waitForConnectorToRegister(AtomicBoolean isRegistered, int timeoutSec) throws Exception{
long startTime = System.currentTimeMillis();
while((System.currentTimeMillis() - startTime) <= timeoutSec*1000 &&
!isRegistered.get()){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(!isRegistered.get()){
throw new Exception("Timed out waiting for connector to register.");
}
}
/**
* Create and add a CredentialsChangedListener to the ManagementConnector.
* It's recommended that you do this before initializing the connector.
* @param managementConnector
* @return the created CredentialsChangedListener
*/
public static CredentialsChangedListener addCredentialsListenerToManagementConnector(ManagementConnector managementConnector, final ContextServiceClient contextServiceClient){
CredentialsChangedListener credentialsChangedListener = new CredentialsChangedListener() {
String connectionData;
@Override
public void credentialsChanged(String newConnectionData) {
LOGGER.info("ConnectionData changed: " + newConnectionData);
connectionData = newConnectionData;
// Connection data is not usually logged due to security considerations.
String hostname = "doctest.example.com";
ConnectorInfoImpl connInfo = new ConnectorInfoImpl(hostname);
ConnectorConfiguration configuration = new ConnectorConfiguration() {{
addProperty("LAB_MODE", true); // exclude this line for production mode
addProperty("REQUEST_TIMEOUT", 10000);
}};
// Notify contextServiceClient that the connection data changed.
contextServiceClient.updateAndReloadConfigAsync(connectionData, connInfo, configuration, null);
}
};
managementConnector.addCredentialsChangedListener(credentialsChangedListener);
return credentialsChangedListener;
}
Client Connector
Before you create a Context Service Client connection, you must:
- Initialize the connector factory.
- Save the
connectionData
string created by Registering Your Application with Context Service.
Before you initialize the Context Service client connection, optionally test your connection using getStatus
. See Check Connection State for more information.
You cannot create both a management connection and client connection on the same Java virtual machine.
To create a Context Service client connection:
- Define a `ContextServiceClient` object and call `ConnectoryFactory.getConnector()` on the `ContextServiceClient.class`.
- (Optional) Define the base configuration for your client by creating a `ConnectorConfiguration` object and adding properties. Create a base configuration only to change the default configuration values. The default values are:
Property | Description | Default Value |
---|---|---|
LAB_MODE | Sets the mode for your organization as either lab mode (true ) or production mode (false ). |
false |
REQUEST_TIMEOUT | Sets the amount of time that the method waits before the request times out. You can timeout create , get , and search methods. |
10,000 ms |
RETRIES | Sets the number of times that the method retries sending a request on a timeout error. | 1 |
Context Service currently supports two modes per organization, production mode and lab mode. You cannot delete objects in production mode. When you are developing your application, set the lab_mode
field to true
. This allows you to delete your example data. For more information, see Flush Object Data.
- Create a connection state listener using the `addStateListener()` method on the Context Service client connector.
- Initialize the client with the `init` method.
This example shows how to create and initialize a Context Service Client connector while using a connection state listener.
Copy/**
* Create and initialize the ContextServiceClient with custom configuration.
* ConnectorFactory should already be initialized.
* @param connectionData
* @return an initialized ContextServiceClient
*/
public static ContextServiceClient createAndInitContextServiceClientWithCustomConfiguration(String connectionData) {
ContextServiceClient contextServiceClient = ConnectorFactory.getConnector(ContextServiceClient.class);
// Add Context Service Client connector state listener. It needs to be done before calling init on the connector
connectorStateListener = addStateListenerToContextConnector(contextServiceClient);
String hostname = "doctest.example.com";
ConnectorInfoImpl connInfo = new ConnectorInfoImpl(hostname);
ConnectorConfiguration configuration = new ConnectorConfiguration(){{
addProperty("LAB_MODE", true); // exclude this line for prod mode
addProperty("REQUEST_TIMEOUT", 10000);
}};
contextServiceClient.init(connectionData, connInfo, configuration);
// Wait 3 sec for connector to be initialized.
try {
waitForConnectorState(connectorStateListener, ConnectorState.REGISTERED, 3);
LOGGER.info(">>>> CS Connector initialized successfully");
}catch(Exception e){
LOGGER.error(">>>> CS Connector FAILED to initialize successfully", e);
}
// Optionally, parse the JSON returned by getStatus for additional status information
String status = contextServiceClient.getStatus();
return contextServiceClient;
}
/**
* Before initializing the connector, create and add a ConnectorStateListener to the ContextServiceClient.
* @param contextServiceClient
* @return the created ConnectorStateListener
*/
public static CustomConnectorStateListener addStateListenerToContextConnector(ContextServiceClient contextServiceClient){
CustomConnectorStateListener stateListener = new CustomConnectorStateListener();
contextServiceClient.addStateListener(stateListener);
return stateListener;
}
/**
* Create a Custom Connector State Listener to override the stateChanged behavior
*/
public static class CustomCSConnectorStateListener implements ConnectorStateListener {
protected ConnectorState connectorState;
public ConnectorState getConnectorState(){
return connectorState;
}
@Override
public void stateChanged(ConnectorState previousState, ConnectorState newState)
{
connectorState = newState;
LOGGER.info("Context Service Client connector state changed: " + newState);
if (newState == ConnectorState.STOPPED) {
// Perform optional cleanup tasks, etc ...
LOGGER.info("Context Service Client connector stopped.");
}else if (newState == ConnectorState.REGISTERED) {
// Perform any actions needed once connector is registered, etc ...
LOGGER.info("Context Service Client connector started.");
} else if (newState == ConnectorState.UNREGISTERED) {
// Perform any actions needed once connector is unregistered, etc ...
LOGGER.info("Context Service Client connector unregistered.");
}
}
}
/**
* Wait timeoutSec for connector to reach a specified state.
*
* This example shows a different technique using CustomConnectorStateListener.getConnectorState() method to determine
* connector state changes.
* @param stateListener
* @param expectedState
* @param timeoutSec
* @throws Exception
*/
public static void waitForConnectorState(CustomCSConnectorStateListener stateListener, ConnectorState expectedState, int timeoutSec) throws Exception{
long startTime = System.currentTimeMillis();
while((System.currentTimeMillis() - startTime) <= timeoutSec*1000 &&
expectedState.equals(stateListener.getConnectorState())){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(!expectedState.equals(stateListener.getConnectorState())){
throw new Exception("Timed out waiting for connector to reach "+ expectedState.name()+"; Current state is :" + stateListener.getConnectorState());
}
}
Update Connection State
To update your connection state call updateAndReloadConfigAsync()
. This method allows the client to dynamically update the connector configuration without having to stop and reinitialize the connection state.
An update is required when you add or change information in your configuration file. updateAndReloadConfigAsync()
is an asynchronous method. Calling this method does not indicate completion. Insert a reloadListener
to listen for completion.
This example shows how to update your connection state.
Copy/**
* Create and add a ReloadListener to a Connector to update and reload.
* @param connector the connector to relaod
* @param connectionData your connectionData string
* @param connectorInfo a ConnectorInfo object
* @param connectorConfiguration a ConnectorConfiguration object
* @return the new reload listener, after a reload has completed
*/
public static ReloadListenerWithWait updateAndReloadConnector(Connector connector, String connectionData, ConnectorInfo connectorInfo, ConnectorConfiguration connectorConfiguration){
ReloadListenerWithWait reloadListener = new ReloadListenerWithWait();
connector.updateAndReloadConfigAsync(connectionData, connectorInfo, connectorConfiguration, reloadListener);
try {
reloadListener.waitForCompletion(60000);
} catch (ApiException apiException) {
if (apiException.getError().getErrorType().equals(ApiErrorType.TIMEOUT_REQUEST)) {
LOGGER.error("Reload timed out!");
} else {
LOGGER.error("Error reloading connector: " + apiException.toString());
}
throw apiException;
}
return reloadListener;
}
Monitor Connection State
Both the management and Context Service client connector have connection states. These states are:
- Unregistered—The connector is unregistered:
- Before the connection has been registered and initialized.
- After the connector has been deregistered.
- Registered—The connection is registered when it is running after being initialized. This is the normal state for a working connection.
- Stopped—The connection is stopped when a registered connection is destroyed using the [
destroy()
] method. Callinit
to change the connection state from stopped to registered.
Check Connection State
Use getStatus
to return a detailed log of connection state information. Use Connection Listeners to listen for a connection change, then perform an action.
If your connection is unregistered, Context Service returns your connection state as OFFLINE
. You can use the lastSavedError
and the Service: CCFS JSON
information to troubleshoot the connection.
In this example, the connection is offline because the proxy host is unknown. You can check your config settings to correct your proxy specification.
Copy/**
* Unregistered Connection State
*/
{
"status":{
"overallStatus":"OFFLINE",
"successfulUpgradeCount":0,
"failureUpgradeCount":0,
"lastSavedError":"RestApiError with errorType: unknownHostError, errorData: /discovery/apps/v1, errorMessage: invalidproxy.cisco.com"
},
"config":{
"staticSdkVersion":"2.0.1",
"extensionSdkVersion":"2.0.2",
"proxy":"http://invalidproxy.cisco.com:80",
"state":"UNREGISTERED"
},
"services":[
{
"name":"ccfs",
"url":"ccfs.ciscoccservice.com",
"ping":{
"status":"NOT_REACHABLE",
"latency":0
}
}
]
}
This example shows the results of getStatus
when the Context Service client is registered and initialized.
Copy/**
* Registered Connection State
*/
{
"status": {
"overallStatus": "ONLINE",
"successfulUpgradeCount": 0,
"failureUpgradeCount": 0
},
"config": {
"appType": "finesse",
"orgId": "11112222-aabb-3333-4444-555666777888"",
"uuid": "zm1n23456-bv7c-8xz9-0123-lk4j567hgf8d",
"staticSdkVersion": "2.0.1",
"extensionSdkVersion": "2.0.2-10318",
"enabledFeatures": [
{
"name": "KMS_ENCRYPTION_KEY"
}
],
"type": "cs_context",
"state": "REGISTERED",
"labMode": true,
"maxRetries": 1,
"cluster": {
"clusterId": "q8gh9sh6-1234-56ab-c7d8-9f012ghj34k5",
"clusterName": "finesse-context-155DA809CBE"
}
},
"services": [
{
"name": "kms",
"url": "encryption-a.wbx2.com",
"ping": {
"status": "200",
"latency": 357
},
"state": "INITIALIZED"
},
{
"name": "fms",
"url": "hercules-a.wbx2.com",
"lastSuccessfulHeartBeatTime": "07:11:16 11:09:28",
"ping": {
"status": "200",
"latency": 365
}
},
{
"name": "dictionary",
"url": "dictionary.produs1.ciscoccservice.com",
"ping": {
"status": "200",
"latency": 220
}
},
{
"name": "ccfs",
"url": "ccfs.ciscoccservice.com",
"ping": {
"status": "200",
"latency": 369
}
},
{
"name": "discovery",
"url": "discovery.produs1.ciscoccservice.com",
"ping": {
"status": "200",
"latency": 187
}
},
{
"name": "ci",
"url": "idbroker.webex.com",
"ping": {
"status": "200",
"latency": 502
}
},
{
"name": "context",
"url": "context-service.produs1.ciscoccservice.com",
"ping": {
"status": "200",
"latency": 221
}
}
]
}
Stop Connection to Context Service
Call the destroy()
method on the client connector to temporarily stop using Context Service in your application. This changes the ConnectorState
to STOPPED
. The destroy()
method does not deregister your service or delete your connection data.
Do not use
destroy()
after you have deregistered your application. Shutting down a management connector usingdestroy()
while continuing to run your application causes errors. If you destroy your management connector, your application loses connectivity to the cloud. You will no longer receive the dynamic SDK jar file or password updates.
To stop your application from authenticating with Context Service, Deregister your application.