clientServerConfigs

GemStone® GemFire® Enterprise

Hierarchical Caching Configuration Example


The clientServerConfigs example uses examples/cacheRunner to demonstrate the common recommended bridge server and bridge client configurations for client/server caching. The example configurations are located under the GemFire installation in examples/dist/clientServerConfigs. For information on client/server caching, see the GemFire Enterprise Developer's Guide and the online Java API documentation.

    In client/server caching, it is vital that the server and client tiers access different sets of data. If the servers and clients access the same distributed region in the same distributed system, the client/server caching model will fail. The servers should be in one distributed system accessing one distributed region. The clients should either all be in a second distributed system or they should be run as standalone applications.

Cache Configuration

The examples/dist/clientServerConfigs directory contains the XML cache configuration files for the bridge server and client. There are two bridge server configuration files. The first is bs_notify_all.xml, which configures the server for the notify-all client entry update behavior. The second is bs_notify_by_subscription.xml, which configures the server for notify-by-subscription behavior. There is one client file, bridge_client.xml. In these files:

    You may need to change the client/server communication port to avoid conflicting with processes already running on your system. If you do, just make sure to change to the same setting for bridge-server port in both of the bs_notify*.xml files, and for the endpoints port settings for both the bridge loader and bridge writer in bridge_client.xml.

Distributed System Configuration

The clientServerConfigs directory also contains subdirectories with gemfire.properties files in them.

These provide the distributed system configurations for the various client/server caching models. They specify the server and client mcast-port settings that define the distributed systems the VMs belong to. For the standalone client, they specify that the VM does not belong to any distributed system.

    You may need to change the mcast-port settings to avoid conflicting with ports that are already in use on your system. If you do, make sure the setting in client_separate_dist_sys/gemfire.properties is still different from the setting in server/gemfire.properties. Do not change any of the settings in client_standalone/gemfire.properties
Follow these instructions to run the configuration examples.

Environment Setup

You will need at least two terminal sessions: one to run a bridge server VM and one to run a bridge client VM. These examples show one server and one client running at a time, but you can increase the number of either. However many you run, you need a separate terminal session for each. For each session, make sure the environment is set according to the instructions in examples/EnvSetup.html. Change directory to examples/dist/clientServerConfigs. The rest of the example instructions are relative to this directory.

Running the Server

To start the server, cd to the server subdirectory and run the server either with the notify-by-subscription XML file or the notify-all file:
    java cacheRunner.CacheRunner ../bs_notify_by_subscription.xml
or
    java cacheRunner.CacheRunner ../bs_notify_all.xml

This starts the CacheRunner program with the server's XML file and the current working directory's gemfire.properties file settings. The xml file defines a region named /root/bridge_region and gives it a loader and a listener. The different configuration files determine how the server updates the client for server-side entry modifications. We'll see more about this at the end of the example.

When the server starts you see this prompt:

    /root>

Enter the commands listed here in bold (most of the output is included in this listing). You define a value, value1, for the first entry, entry1, and you cause a loader invocation by getting an entry, entry2, that doesn't exist in the cache. The loader is programmed to return a value that is the string equivalent of the key. The listener reports on all entry creations, and specifies whether the value came from a load operation.

    /root> chrgn bridge_region
    /root/bridge_region> put entry1 value1
    CacheListener.afterCreate EntryEvent on region /root/bridge_region
      [distributed, not expiration, local origin]
      [not load]
      Key: entry1
      Old value: null
      New value: value1

    /root/bridge_region> get entry2
    CacheListener.afterCreate EntryEvent on region /root/bridge_region
      [distributed, not expiration, local origin]
      [local load]
      Key: entry2
      Old value: null
      New value: entry2

         entry2 -> String: "entry2"
    /root/bridge_region> ls
    Region Entries:
         entry2 -> String: "entry2"
         entry1 -> byte[]: "value1"

    Subregions:
    /root/bridge_region>
Leave this running. The listener will report when there are modifications to the server's bridge_region.

Running the Client

You can follow these instructions for either of the client subdirectories to see the client/server cache working. Choose the client directory whose configuration you wish to try, cd to it, and run the following:
    java cacheRunner.CacheRunner ../bridge_client.xml
Once the client is started, enter the commands below. You can skip the registerInterest step if you are running the server with the notify-all XML file.
    /root> chrgn bridge_region
    /root/bridge_region> registerInterest entry1
    Interest registered in entry1 
    /root/bridge_region> get entry1
         entry1 -> byte[]: "value1"
    /root/bridge_region> get entry2
         entry1 -> byte[]: "entry2"
    /root/bridge_region> des entry2     
    /root/bridge_region> get entry3
         entry3 -> String: "entry3"
    /root/bridge_region> put entry1 valueFromClient
    /root/bridge_region>

As you run these commands, note the output in the server VM. When you ask for an entry that exists in the server, like entry1, it is passed directly from the server cache. When you ask for an entry that the server does not have, like entry3, it invokes its loader, updates the server cache, and returns the result. When you put an entry on the client side, the new value is passed up to the server. You can run 'ls' on both the server and client to verify the cache contents. Both sides should report the same thing:

    /root/bridge_region> ls
    Region Entries: 
    entry2 -> String: "entry2"
    entry3 -> String: "entry3"
    entry1 -> byte[]: "valueFromClient"

With notify-by-subscription, the server automatically sends entry update notification, with values, for the entries the client has registered interest in. For entries that are not registered, the client can get values explicitly, but receives no updates for server-side update operations. With notify-all, the server automatically sends entry update notifications to the client as invalidations for all entries, with no values passed. Then if the client needs the value, the get operation will retrieve it from the server.

To see this, from the server side, update entry1 and entry2:

    /root/bridge_region> put entry1 serverValue1
        ...
    /root/bridge_region> put entry2 serverValue2
        ...

Final Client Cache for Notify-By-Subscription

For notify-by-subscription, the client side entry1 has the new server value. This is because the client registered interest in entry1 at the beginning of this session. The other entry, entry2, is not changed, so at this point it does not match the server cache. Since the client did not register interest in it, the server does not propagate any information on the server-side modification.

To see this, run 'ls' on the client side:

    /root/bridge_region> ls
    entry2 -> String: "entry2"
    entry3 -> String: "entry3"
    entry1 -> byte[]: "serverValue1"

Final Client Cache for Notify-All

For notify-all, both entry1 and entry2 are invalidated on the client.

To see this, run 'ls' on the client side:

    /root/bridge_region> ls
    entry2 -> String: No value in cache.
    entry3 -> String: "entry3"
    entry1 -> byte[]: No value in cache.

Running the Server and Client with Authentication/Authorization Enabled

Follow the instructions in this section to run the example with authentication and authorization enabled.

Running the Server with Authentication/Authorization Enabled

To start the server, cd to the server subdirectory and make a copy of the gemfire.properties file so the file can be restored to its original, unedited state if needed.

  1. Set the following security properties in the gemfire.properties file.
    	security-client-authenticator=templates.security.DummyAuthenticator.create
    	security-client-accessor=templates.security.XmlAuthorization.create
    	security-authz-xml-uri=authz-dummy.xml
    
    Refer to the Sample GemFire Security Implementations section for other options for configuing and launching the server with CacheRunner.

  2. Run the server with the notify-by-subscription XML file:
        java cacheRunner.CacheRunner ../bs_notify_by_subscription.xml
    

    This starts the cache runner program with the server's XML file and the current working directory's gemfire.properties file settings. The xml file defines a region named /root/bridge_region and gives it a loader and a listener. The different configuration files determine how the server updates the client for server-side entry modifications.

    Once the server has started, you will see this prompt:

        /root>
    
  3. Enter the commands listed here in bold (most of the output is included in this listing).
        /root> chrgn bridge_region
        /root/bridge_region> put entry1 value1
        CacheListener.afterCreate EntryEvent on region /root/bridge_region
          [distributed, not expiration, local origin]
          [not load]
          Key: entry1
          Old value: null
          New value: value1
    
        /root/bridge_region> ls
        Region Entries:
             entry1 -> byte[]: "value1"
    
        Subregions:
        /root/bridge_region
    Leave this running. The listener will report when there are modifications to the server's bridge_region.

Running the Client with Authentication/Authorization Enabled

These instructions apply for either of the client subdirectories. To see the client/server cache working with authentication and authorization, choose the client directory whose configuration you wish to try and then cd to it. Make a copy of the gemfire.properties file so the file can be restored to its original, unedited state if needed

With Valid Credentials:

  1. Set the following security properties in the client's gemfire.properties file:
        security-client-auth-init=templates.security.UserPasswordAuthInit.create
        security-username=reader1
        security-password=reader1
  2. Run the client with the following command:
    	java cacheRunner.CacheRunner ../bridge_client.xml
    
  3. Once the client is started, enter the commands below.
    	/root> chrgn bridge_region
    /root/bridge_region> ls
    Region Entries: Subregions: /root/bridge_region> get entry1 entry1 -> byte[]: "value1"
    /root/bridge_region> ls Region Entries: entry1 -> byte[]: "value1" Subregions: /root/bridge_region> put key2 value2 com.gemstone.gemfire.cache.CacheWriterException: Connection@1766806[id=0;localPort=44027;
    timeout=10000;Endpoint@1d36dfe[name=server;host=localhost;port=40404;
    type=PRIMARY;numCnxs=1;qSize=0]]: While performing a remote put,
    caused by com.gemstone.gemfire.security.NotAuthorizedException:
    Not authorized to perform PUT operation on region [/root/bridge_region]

    The error message reports that the put operation is not authorized. Note that authorization is granted for the get operation.

With Invalid Credentials:

  1. Change the security-username and security-password properties in gemfire.properties to start the client with invalid credentials:
    	security-client-auth-init=templates.security.UserPasswordAuthInit.create
    	security-username=invalidUsr
    	security-password=invalidUsr
    
  2. Now start the client. The following exception should occur:

    java cacheRunner.CacheRunner ../bridge_client.xml
    [security-warning 2008/03/27 20:29:29.990 IST
    tid=0x1] Connection to
    Endpoint@1d6a56e[name=server;host=localhost;port=40404;type=NORMAL;numCnxs=1]:
    com.gemstone.gemfire.security.AuthenticationFailedException:
    DummyAuthenticator: Invalid user name [reader1], password supplied].


With No Credentials
:

Remove the security-client-auth-init, security-username and security-password properties from gemfire.properties to start the client without any credentials. The following exception occurs:

java cacheRunner.CacheRunner ../bridge_client.xml

[security-warning 2008/03/27 20:38:44.338 IST tid=0x1]
Connection to Endpoint@1da669c[name=server;host=localhost;port=40404;type=NORMAL;numCnxs=1]:
com.gemstone.gemfire.security.AuthenticationRequiredException: No security-* properties are provided]


Sample GemFire Security Implementations

There are two aspects to GemFire security:

  1. Authentication: Handles the authentication of nodes in a peer-to-peer network, as well as clients that connect to the servers.
  2. Authorization : Evaluates the permission for GemFire operation(s) by the client(s).

Authentication can be either Dummy, LDAP server-based, or PKCS-based, whereas authorization is XML-based only. For different authentication schemes, the corresponding authorization XML configuration file should be provided to the Sample Authorization module.

Dummy Authentication

This scheme of authentication is based on a simple username and password. The server side authenticator is in package templates.security.DummyAuthenticator.create. The client side initializer for it is in templates.security.UserPasswordAuthInit.create.

Mapping between Authentication credentials and Authorization roles & privileges
Authentication User Name Authorization Roles Permission to the Roles
root, admin, administrator reader, writer, cacheOps
reader     : get, register_interest, unregister_interest, key_set, contains_key
writer      : put, destroy, region_clear
cacheOps: query, execute_cq, stop_cq, close_cq, region_create, region_destroy
reader0, reader1, reader2 reader
reader     : get, register_interest, unregister_interest, key_set, contains_key
writer0, writer1, writer2 writer
writer      : put, destroy, region_clear
reader3, reader4 queryRegions
queryRegions: query, execute_cq, stop_cq, close_cq


LDAP Authentication

This authentication scheme is based on the usernames and password configured in a LDAP server. Refer to the Security chapter in the GemFire Enterprise System Administrator's Guide for more details. The server side authenticator is in package templates.security.LdapUserAuthenticator.create The client side initializer for it is in templates.security.UserPasswordAuthInit.create.

Mapping between Authentication credentials and Authorization roles & privileges
Authentication User Name Authorization Roles Permission to the Roles
gemfire1, gemfire2 reader, writer, cacheOps
reader     : get, register_interest, unregister_interest, key_set, contains_key
writer      : put, destroy, region_clear
cacheOps: query, execute_cq, stop_cq, close_cq, region_create, region_destroy
gemfire3, gemfire4, gemfire5 reader
reader     : get, register_interest, unregister_interest, key_set, contains_key
gemfire6, gemfire7, gemfire6 writer
writer      : put, destroy, region_clear
gemfire9, gemfire10 queryRegions
queryRegions: query, execute_cq, stop_cq, close_cq


PKCS Authentication

This scheme of authentication is based on public-private key based encryption/decryption. Refer to the Security chapter in the GemFire Enterprise System Administrator's Guide for keystore configurations. The server side authenticator is in package templates.security.PKCSAuthenticator.create. The client side initializer is in templates.security.PKCSAuthInit.create.

PKCS configuration details:

  1. Create a keystore for aliases from gemfire1 to gemfire10, as described in the Security chapter for the GemFire Enterprise System Administrator's Guide.

  2. Provide the following properties in gemfire.properties for client side configuration.
    Note: All of these properties are user-provided information to keytool-like utilities during public/private key generation and self-signing.

    % security-keystorepath=<absolute filepath to keystore where keys are generated>
    % security-alias=<alias name given while generating Public & Private key pair for the Client>
    % security-keystorepass=<password entered while generating Private key while Self-signing>

  3. Provide the following properties in gemfire.properties for server side configuration.
    Note: All of these properties are user-provided information to keytool-like utilities during TrustStore generation.

    % security-publickey-filepath=<absolute filepath to keystore where public keys are generated>
    % security-publickey-pass=<password entered while generating key to TrustStore>

Mapping between Authentication credentials and Authorization roles & privileges
Authentication KeyStore Aliases Authorization Roles Permission to the Roles
gemfire1, gemfire2 reader, writer, cacheOps
reader     : get, register_interest, unregister_interest, key_set, contains_key
writer      : put, destroy, region_clear
cacheOps: query, execute_cq, stop_cq, close_cq, region_create, region_destroy
gemfire3, gemfire4, gemfire5 reader
reader     : get, register_interest, unregister_interest, key_set, contains_key
gemfire6, gemfire7, gemfire6 writer
writer      : put, destroy, region_clear
gemfire9, gemfire10 queryRegions
queryRegions: query, execute_cq, stop_cq, close_cq


XML-Based Authorization

This authorization scheme is based on the prior mapping of authentication credentials to roles and privileges. Permissions in XML files are supplied corresponding to the authentication scheme. Refer to the Security chapter in the GemFire Enterprise System Administrator's Guide for further information. The server side security-accessor is in package templates.security.XmlAuthorization.create, and security-authz-xml-uri should point to either authz-dummy.xml or authz-ldap.xml, depending on the security-authenticator provided.

Additional Example Scenarios

There are a number of scenarios you can explore with this basic setup. You might add other clients and see the effect on client peers when a client updates an entry. You could see how the behavior changes when the bridge writer's cache callback is disabled (in the client's XML configuration file, change the true value to false for the "establishCallbackConnection" parameter).