See wam Menu

Using the CamsClient

This section describes the basic tasks involved in using the CamsClient.

For details on how to use each of the services available from the CamsClient, see Using CamsClient Services.

Step 1 - Creating a CamsClient

Creating a CamsClient involves the following tasks:

The first step to using a CamsClient is creating the Config object required to initialize it. The Config object contains initialization parameters and a Logger instance. The CamsClient uses a Java Properties object for configuration with contents that may vary depending on the referenced classes.

Without digging into specific configuration Properties yet, Example 1 shows the general approach for creating and initializing a CamsClient.

import com.cafesoft.cams.client.CamsClient;

import com.cafesoft.cams.Config;
import com.cafesoft.cams.ConfigException;
import com.cafesoft.cams.StandardConfig;

import com.cafesoft.core.log.Logger;
import com.cafesoft.core.log.StderrLogger;

import com.cafesoft.security.common.client.StandardCamsClientFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import java.util.Properties;

...

CamsClient camsClient = null;

try
{
   ...

  // Create a Config object.
  Config config = createConfig(new File("./cams-client-example.conf"));

  // Create and initialize a CamsClient instance
  camsClient = createCamsClient(config);

  ...
}
catch (ConfigException e)
{
  // Error initializing CamsClient
  e.printStackTrace();
}
catch (FileNotFoundException e)
{
  // Configuration file does not exist
  e.printStackTrace();
}
catch (IOException e)
{
  // Error reading configuration file
  e.printStackTrace();
}

...

Example 1 - Creating and Initializing the CamsClient

Step 1a - Creating a Config Object

The CamsClient uses a Config object to access initialization parameters, a Logger, and basic information about the context in which it operates.

Example 2 shows how a generic Config object can be created from a Properties object and a Logger instance.

/**
   * Create a Config object from a File.
   *
   * @param configFile the configuration file.
   * @return an initialized CamsClient instance.
   * @exception ConfigException if a CamsClient configuration error.
   */
  public static Config createConfig(File configFile)
    throws ConfigException
  {
    // Load configuration properties from a file.
    Properties properties = loadPropertiesFromFile(configFile);

    // Create the Logger.
    Logger logger = createLogger(properties);

    return new StandardConfig(properties, "", null, logger);
  }

Example 2 - Creating the Config Object

Loading Configuration Properties from a File

Example 3 shows generic code for loading configuration properties from a file. The configuration file path is generally configured in a platform-specific way.

For example, the WAM Apache 2 web agent configures the path using a directive in the Apache httpd.conf file, the WAM web agent for Tomcat uses the server.xml file and catalina.home system property, and the WAM IIS web agent uses a path relative to the cams virtual directory configured in the IIS admin GUI.

The configuration file contents may vary widely based on the number of WAM policy servers configured, the type of Logger configured, the services needed, the agent type, and so forth. Consequently, this content shows small segments of possible configuration properties in the client API contexts in which they apply.

You may want to review the complete cams-client-example.conf file provided with the WAM Client API example code and specific WAM web agent configuration files.

Contact OneLogin support for answers to specific configuration property questions.

 /**
   * Load the configuration Properties from a File.
   *
   * @param configFile the configuration File object.
   * @return a new Properties object with all of the appropriate properties.
   */
  private static Properties loadPropertiesFromFile(File configFile)
    throws ConfigException
  {
    Properties properties = new Properties();
    FileInputStream fis = null;

    try
    {
      fis = new FileInputStream(configFile);
      properties.load(fis);
    }
    catch (FileNotFoundException e)
    {
      throw new ConfigException("Error loading configuration file: " + 
        configFile.getAbsolutePath(), e);
    }
    catch (IOException e)
    {
      throw new ConfigException("Error loading configuration file: " + 
        configFile.getAbsolutePath(), e);
    }
    finally
    {
      try
      {
        if (fis != null)
          fis.close();
      }
      catch (Exception e)
      {
      }
    }

    return properties;
  }

Example 3 - Loading Configuration Properties from a File

Creating a Logger

The CamsClient depends on access to a WAM Logger object from within the Config object. The Logger is used for logging FATAL, ERROR, WARNING, INFO, and DEBUG-level messages and is generally the same Logger used by the enclosing WAM agent.

The code in Example 4 shows how a Logger can be created and initialized using Properties loaded from the configuration file.

The Logger implementation class is specified by property logger.class, which generally uses the CamsTraceLogger implementation as shown below.

logger.class=com.cafesoft.cams.log.CamsTraceLogger
logger.file.path=./logs/cams-client-example.log
logger.file.append=true
logger.file.bufferedIO=true
logger.file.bufferSize=8192
logger.file.maxSize=10MB
logger.file.maxBackupIndex=100
logger.enableConsole=true
logger.enableDebugFilter=false
logger.verbose=false
logger.debug=false

The CamsTraceLogger is a specialized Logger implementation that will send INFO and DEBUG-level messages to a configured file and WARNING, ERROR, and FATAL messages to the file and to the System.err stream. Configuration properties for this logger are documented in the sample cams-client-example.conf file provided in the WAM Client API example code and the WAM Javadoc.

Example 4 shows how the WAM StderrLogger implementation can be returned if creation of the Logger specified in the configuration file fails. See the WAM Javadoc for com.cafesoft.core.log.StderrLogger for more details.

NOTE: Additional Logger implementations are available for use in the com.cafesoft.core.log Java package.

 /**
   * Create the Logger for use by the CamsClient.
   *
   * @param properties the configuration properties.
   * @return a new Logger instance.
   */
  private static Logger createLogger(Properties properties)
  {
    Logger logger = StderrLogger.DEFAULT_STDERR_LOGGER;
    String className = properties.getProperty("logger.class");

    try
    {
      properties.put("logger.name", "cams-client-example");
      logger = (Logger)ClassUtils.createInstance(className);
      logger.initialize(properties);
    }
    catch (ClassInstantiationException cie)
    {
      StringBuffer buffer = new StringBuffer();
      buffer.append("Could not create Logger object, reason=");
      buffer.append(cie.getMessage());
      buffer.append(" creating StderrLogger");
      logger = StderrLogger.DEFAULT_STDERR_LOGGER;
      logger.warning(CamsClientExample.class, buffer.toString());
    }
    catch (LoggerException le)
    {
      StringBuffer sb = new StringBuffer();
      sb.append("Could not initialize Logger object, ");
      sb.append("reason=");
      sb.append(le.getMessage());
      sb.append(", Creating StderrLogger");
      logger = StderrLogger.DEFAULT_STDERR_LOGGER;
      logger.warning(CamsClientExample.class, sb.toString());
    }

    return logger;
  }

Example 4 - Creating the Logger

Step 1b - Creating and Initializing a CamsClient

Once a Config object has been created, creation and initialization of a CamsClient instance is quite simple using a CamsClientFactory as shown in Example 5.

The StandardCamsClientFactory creates a CamsClient instance using the value of initialization parameter cams.client.class, which is generally provided in the agent configuration file.

cams.client.class=com.cafesoft.security.common.client.StandardCamsClient

The CamsClient instance is initialized in Example 5 with camsClient.initialize(config);, which throws a ConfigException if any required initialization parameters are missing or any supplied parameters are invalid.

 /**
   * Create and initialize a CamsClient instance.
   *
   * @param config the Config object.
   * @return an initialized CamsClient instance.
   * @exception ConfigException if a CamsClient configuration error.
   */
  public static CamsClient createCamsClient(Config config)
    throws ConfigException
  {
    CamsClient camsClient = null;

    CamsClientFactory clientFactory = new StandardCamsClientFactory();
    camsClient = clientFactory.create(config);
    camsClient.initialize(config);

    return camsClient;
  }

Example 5 - Creating and Initializing a CamsClient

Step 2 - Connecting to the WAM Policy Server(s)

Using a CamsClient, connecting to WAM Policy Server(s) is accomplished using the connect() method as shown in Example 6.

If the client cannot connect, an ERROR is logged and a ConnectionException is delivered to registered ConnectionExceptionListeners.

The StandardCamsClient implementation is designed to:

  • Eagerly connect to WAM Policy Server(s) when connect() is invoked.

  • Automatically reconnect to the WAM Policy Server(s) if a connection is broken.

import com.cafesoft.cams.client.CamsClient;
import com.cafesoft.cams.client.ConnectionException;
import com.cafesoft.cams.client.ConnectionExceptionListener;

...

   // Connect to the WAM Policy Server(s)
   camsClient.connect();

...

Example 6 - Connecting to the WAM Policy Server using CamsClient

If the CamsClient cannot communicate with the WAM Policy Server when using one of the WAM services due to a broken or nonexistent connection, a service-specific Exception will be thrown. See Using CamsClient Services for more details.

The following sections describe how the StandardCamsClient is configured to connect with one or more WAM Policy Servers.

Configuring the WAM Policy Server URL(s)

Using the StandardCamsClient implementation, connections to WAM Policy Server(s) are configured using initialization parameters of the form cams.server.url.<name>=<url> where <name> is a registered WAM Policy Server name and <url> is the fully-qualified URL to the host and port where the WAM Policy Server is listening for client connections.

For example, cams.server.url.MyCamsServer=cams://localhost:9191 uses <name>=MyCamsServer and <url>=cams://localhost:9191.

This is the default value that you’ll find in all cams-webagent.conf files. Please note that the protocol is cams, not http or https. The cams protocol is an optimized, proprietary, binary format designed for speed and security. Future CamsClient implementations may support other protocols, including https and other secure protocols.

The following WAM Policy Server initialization parameters configure two WAM Policy Servers named Orville and Wilbur on remote hosts:

cams.server.uri.Orville=cams://orville.mydomain.com:9191
cams.server.url.Wilbur=cams://wilbur.mydomain.com:9191

Configuring the WAM Policy Server Cluster Name

All WAM Policy Servers configured for a given CamsClient instance must be members of the same cluster, which is configured using the cams.cluster.name initialization parameter: cams.cluster.name=MyCamsCluster

This value must be identical for the CamsClient and the WAM Policy Server to which a connection is attempted. A mismatch causes a connection authentication failure.

Configuring the Number of Connections per Policy Server

From a system resource perspective, TCP/IP sockets are a rather expensive resource to create and clean up. Consequently, the WAM messaging framework was designed to enable many client threads to multiplex messages over a single WAM connection. This saves system resources and increases system scalability and performance.

Each connection is capable of handling all WAM service protocols and multiplexing requests and responses for many concurrent client threads. Each client thread uses a thread-safe “Cams Message Socket,” which is described in the next section.

Connections to WAM Policy Servers are managed in an “object pool”, which is configured with a minimum and maximum number of connections using the following values:

connection.pool.minium=2
connection.pool.maximum=5

As additional connections are needed, they are added in increments configured by the connection.pool.increment=1 parameter.

The details on how and when new connections are created and existing connections are shutdown based on connection pool minimum and maximum sizes are implementation specific and cannot be controlled via the CamsClient API (or any other existing API).

The actual number of connections needed for a given client environment may vary widely based on the number of WAM Policy Servers in a cluster, system load, agent type, the mix of WAM services used, and the other network services on which they depend (like LDAP servers, RDBMS servers, etc).

At present, the only reliable way to determine suitable values for “worst case” scenarios is to load test in a simulated environment. That said, the following guidelines usually apply:

  • Each connection can generally handle 10 to 20 client threads.

  • A pool increment of 1 or 2 is generally sufficient.

Connections may be established using an “eager” or a “lazy” paradigmn depending on the value of the following initialization parameter: connection.pool.lazyConnect=false

A value of false indicates use of eager connections, which are created when CamsClient.connect() is invoked.

A value of true causes delayed connection creation until the first WAM service request is attempted. Use of an eager connection paradigm is recommended as connectivity problems are more easily diagnosed and minimal connections resources will be ready and waiting to expedite WAM service requests.

Configuring the Number of Message Sockets per Connection

The WAM message framework multiplexes messages from many different client threads using the concept of a “Message Socket.” A single WAM connection may establish 10 or 20 reusable message sockets.

During the scope of a client service request, a message socket is transparently reserved and used as a endpoint for request/response messages. A service request sent by a WAM client will be transported over the connection associated with a given message socket and will be handled by the receiving end of the message socket by the WAM Policy Server, where it is dispatched to a service based on content type.

Though communications are asynchronous, the WAM Client API hides this detail and appears to block until a response is received or a configurable timeout period is exceeded.

The number of message sockets per WAM connection is configured using the following initialization parameter: connection.pool.socket.maximum=10

The default value of 10 message sockets per connection is rarely changed, though values of 5-30 have been used depending on various environment performance characteristics.

Using multiple WAM connections, each with multiple message sockets, the WAM message framework attempts to load balance requests across connections. Additional connections and message sockets are created as needed based on system load.

From a resource allocation perspective, the goal is to allocate a sufficient number of connections and message sockets for the typical load handled through a given agent, while providing additional capacity for traffic spikes.

For example, the following connection-related initialization parameters are generally sufficient for an environment that handles 20 requests per second, but may handle up to 200 requests per second:

connection.pool.lazyConnect=false connection.pool.minimum=2 connection.pool.maximum=20 connection.pool.increment=1 connection.pool.socket.maximum=10

Configuring the Client Authentication Credentials

WAM clients are authenticated by a WAM Policy Server whenever a new connection is established. The WAM message framework sends encrypted username, password, and the WAM cluster name as encrypted parameters using the configured secret key parameters configured for the WAM client (see section: Configuring Secret Key Parameters).

connection.authentication.type=EncryptedParameters
connection.authentication.principal=cams-web-agent
connection.authentication.credential=password

The WAM system security domain generally authenticates agents and by default the principal name and credential correspond to a username/password configured in the cams-users.xml configuration file, though agent accounts can be set up and managed through any WAM LoginModule (e.g. LDAP, RDBMS, etc.).

Configuring Secret Key Parameters

WAM uses symmetric secret key PKI to encrypt/decrypt sensitive values, such as authentication credentials, sent between WAM web agents and a WAM policy server. The following initialization parameters must match those configured for your WAM Policy Server(s):

cams.skey.algorithm=Blowfish cams.skey.key=ed28f2c7b60e978277d125d774bd25c1cad3c5c1a7f02757 cams.skey.iv=1a5dce1235fd429e

NOTE: The displayed secret key values MUST be changed a production environment to secure communications in your environment.

Please consult Securing Cams Communications using Secret Keys for more details.

Handling ConnectionExceptions

If your agent needs to detect and handle connection problems, you can create and register a ConnectionExceptionListener with the CamsClient. Regardless of whether you do so, INFO, WARNING, ERROR, and FATAL messages will be logged.

Example 7 shows how you might create and handle a ConnectionException by registering a ConnectionExceptionListener.

Due to the intrinsic connection management, load balancing, and connection recovery capabilities of the WAM message framework, ConnectionExceptionListener implementations are generally used to notify administratators that a connectivity problem may exist.

For example, if a single WAM Policy Server node temporarily becomes unavailable due to a system failure or networking issue, a ConnectionException will be delivered to registered ConnectionExceptionListeners, even though a second WAM Policy Server node is still accessible.

import com.cafesoft.cams.client.CamsClient;
import com.cafesoft.cams.client.ConnectionException;
import com.cafesoft.cams.client.ConnectionExceptionListener;

...

   // Add a ConnectionExceptionListener
   camsClient.addConnectionExceptionListener(new MyConnectionExceptionListener());

   // Connect to the WAM Policy Server
   camsClient.connect(); 

public class MyConnectionExceptionListener implements ConnectionExceptionListener
{
   public handleConnectionException(ConnectionException e)
   {
      System.err.println("[ERROR] Received ConnectionException: msg=" + e.getMessage());

      // Notify an administrator via e-mail?
      ...
   }
}

Example 7: Handling ConnectionExceptions using a ConnectionExceptionListener

Testing the CamsClient Connection State

The CamsClient API provides a way to check the “connection state”, which really means that the client:

  • Has one or more active connections to WAM Policy Servers.

  • If configured with the lazy connection paradigm, will attempt to connect on the first WAM service request.

  • If configured with the eager connection paradigm, is attempting to connect to configured WAM Policy Servers.

  • If connectivity has been lost, is attempting to reconnect to configured WAM Policy Servers.

This may seem a little confusing, but the WAM client and message framework was designed to provide reliable messages services, including failover and recovery transparently at the client API level.

If necessary, the recommended way for WAM agent-level code to test for connectivity to specific WAM Policy Servers is using the WAM “ping” service.

NOTE: The standard CamsClient Context implementation transparently uses the “ping” service to monitor WAM Policy Servers and provide failover/recovery functionality.

Example 8 shows how the CamsClient API can be used to test the connection state. The .isConnected() method will return true after invocation of .connect() and before invocation of .disconnect(), whether or not an active TCP/IP connect to a WAM Policy Server exists.

import com.cafesoft.cams.client.CamsClient;

...

   // Test the CamsClient for connection state
   boolean isConnected = camsClient.isConnected(); 
   
...

Example 8: Checking the CamsClient connection state

Step 3 - Finding a WAM Service

The services available via a CamsClient are generally configured in its initialization properties and are looked up via the CamsClient by:

  1. type - Specified in the Java API as a Java Class

  2. id - a String token

Before an agent can look up and use a WAM service, the CamsClient must be initialized and connected.

Example 9 contains a code fragment showing how an agent can lookup the WAM service used for authenticating users.

import com.cafesoft.cams.client.CamsClient;
import com.cafesoft.cams.client.ConnectionException;
import com.cafesoft.cams.client.ConnectionExceptionListener;

import com.cafesoft.cams.ConfigException;

import com.cafesoft.cams.auth.AuthenticationService;

import com.cafesoft.core.service.ServiceException;

import com.cafesoft.core.log.Logger;

...

try
{
...
   // Connect to the WAM Policy Server
   camsClient.connect();

   // Lookup the WAM AuthenticationService
   AuthenticationService authService = (AuthenticationService) camsClient.find
   "authentication", AuthenticationService.class);

...

}
catch (ConfigException e)
{
   logger.error(this, "CamsClient configuration error", e);
}
catch (ServiceException e)
{
   logger.error(this, "Error finding a Cams service", e);
}

Example 9 - Finding the WAM AuthenticationService by id and type

The common services (including the service identifier and type) available from a CamsClient via the Java API may include the following:

Service id Service type Description
access-control com.cafesoft.cams.access.AccessControlService Used to check access to protected resources
authentication com.cafesoft.cams.auth.AuthenticationService Used to authenticate users
session-access com.cafesoft.cams.session.access.SessionAccessService Used to get information about authenticated user sessions
session-control com.cafesoft.cams.session.control.SessionControlService Used to close authenticated user sessions
ping com.cafesoft.cams.ping.PingService Used to check connectivity with a WAM Policy Server

Table 1 - Summary of common services in the WAM Agents Java API

Check the CamsClient configuration for the actual services registered, their identifiers and types. For details on how to use each of these services, see Using CamsClient Services.

Step 4 - Disconnecting from the WAM Policy Server

The CamsClient should be gracefully disconnected from the WAM Policy Server when an agent exits or restarts. This gives the CamsClient a chance to:

  1. Disconnect active network connections.

  2. Clean up allocated service resources.

Example 10 shows a CamsClient disconnecting.

import com.cafesoft.cams.client.CamsClient;

...

   // Disconnect from the WAM Policy Server
   camsClient.disconnect();

...

Example 10 - Disconnecting a CamsClient

Once disconnected, it is illegal to lookup and use services. However, it is possible to reconnect by invoking the CamsClient connect() method.

Step 5 - Destroying the CamsClient

Destruction of the CamsClient causes disconnection and cleanup of all network connections, services, and other resources.

Once destroyed, it is illegal to use the CamsClient and any of its services. A destroyed CamsClient cannot be reinitialized and reconnected.

Example 11 shows how to destroy a CamsClient.

import com.cafesoft.cams.client.CamsClient;

...

   // Gracefully disconnect
   camsClient.disconnect();

   // Destroy the CamsClient
   camsClient.destroy();
   camsClient = null;

...

Example 11 - Disconnecting a CamsClient


Have a Question?

Have a how-to question? Seeing a weird error? Contact us.

Found a bug? Submit a support ticket.

Have a product idea or request? Share it with us in our Ideas Portal.