A newer version of IMDG is available.

View latest

Want to try Hazelcast 5.0 beta?

We’ve combined the in-memory storage of IMDG with the stream processing power of Jet to bring you an all new Hazelcast platform.

Deploying User Code from Clients

You can also deploy your code from the client side for the following situations:

  1. You have objects that run on the cluster via the clients such as Runnable, Callable and EntryProcessor.

  2. You have new user domain objects which need to be deployed into the cluster.

When this feature is enabled on the client, the client will deploy the classes to the members when connecting. This way, when a client adds a new class, the members do not require a restart to include it in their classpath.

You can also use the client permission policy to specify which clients are permitted to use User Code Deployment. See the Permissions section.

Configuring Client User Code Deployment

Client User Code Deployment feature is not enabled by default. You can configure this feature declaratively or programmatically.

Using the user code deployment feature is a fit for your functional objects like Runnable, Callable and EntryProcessor. For the domain objects, we recommend you to use the generic object interface (GenericRecord).

Following are example configuration snippets:

Declarative Configuration:

In your hazelcast-client.xml/yaml:

  • XML

  • YAML

<hazelcast-client>
    ...
    <user-code-deployment enabled="true">
        <jarPaths>
            <jarPath>/User/example/example.jar</jarPath>
            <jarPath>example.jar</jarPath> <!--from class path -->
            <jarPath>https://com.example.com/example.jar</jarPath>
            <jarPath>file://Users/example/example.jar</jarPath>
        </jarPaths>
        <classNames>
            <!-- for classes available in client's class path -->
            <className>example.ClassName</className>
            <className>example.ClassName2</className>
        </classNames>
    </user-code-deployment>
    ...
</hazelcast-client>
hazelcast-client:
  user-code-deployment
    enabled: true
    jarPaths:
      - /User/example/example.jar
      - example.jar
      - https://com.example.com/example.jar
      - file://Users/example/example.jar
    classNames:
      - example.ClassName
      - example.ClassName2

Programmatic Configuration:

        ClientConfig clientConfig = new ClientConfig();
        ClientUserCodeDeploymentConfig clientUserCodeDeploymentConfig = new ClientUserCodeDeploymentConfig();

        clientUserCodeDeploymentConfig.addJar("/User/example/example.jar");
        clientUserCodeDeploymentConfig.addJar("https://com.example.com/example.jar");
        clientUserCodeDeploymentConfig.addClass("example.ClassName");
        clientUserCodeDeploymentConfig.addClass("example.ClassName2");

        clientUserCodeDeploymentConfig.setEnabled(true);
        clientConfig.setUserCodeDeploymentConfig(clientUserCodeDeploymentConfig);

Important to Know

The members have to be configured in a specific way for the feature to work correctly:

  • User Code Deployment must be enabled on the members. Otherwise, the classes from the client will be ignored. Also blacklisted and non-whitelisted classes will be ignored.

  • All members must be providers, provider-mode must be set to LOCAL_AND_CACHED_CLASSES on all members.

  • No provider-filter must be configured.

The client uploads the classes only to one member. If the members don’t load classes from each other, other members won’t see the class.

Here’s a programmatic configuration of the members that will work with client user code deployment:

        Config config = new Config();
        UserCodeDeploymentConfig ucdConfig = config.getUserCodeDeploymentConfig();
        ucdConfig.setEnabled(true);
        // following two configs are defaults, we show them for clarity
        ucdConfig.setProviderMode(ProviderMode.LOCAL_AND_CACHED_CLASSES);
        ucdConfig.setProviderFilter(null);

See the Member User Code Deployment section for more information on enabling it on the member side and the configuration properties.

Classes deployed from clients are always cached on the members, no matter whether ETERNAL or OFF is configured on the members.

Performance Considerations

The client always uploads all added classes and jars to one of the members, whether it has them or not. So avoid adding large jar files for each connection - if configured properly, the member will have the class the next time the client connects.

Two Versions of a Class

If the client uploads a class and the member already has that class, an exception is thrown if the byte code is different. If byte code is same, it is ignored. Therefore classes uploaded from the client can’t be updated with a new version.

Adding User Library to CLASSPATH

When you want to use a Hazelcast feature in a non-Java client, you need to make sure that the Hazelcast member recognizes it. For this, you can use the /user-lib directory that comes with the Hazelcast package and deploy your own library to the member. Let’s say you use Hazelcast Node.js client and want to use an entry processor. This processor should be IdentifiedDataSerializable or Portable in the Node.js client. You need to implement the Java equivalents of the processor and its factory on the member side, and put these compiled class or JAR files into the /user-lib directory. Then you can run the start.sh script which adds them to the classpath.

The following is an example code which can be the Java equivalent of entry processor in the Node.js client:

public class IdentifiedEntryProcessor implements EntryProcessor<String, String, String>, IdentifiedDataSerializable {
    static final int CLASS_ID = 1;
    private String value;
    public IdentifiedEntryProcessor() {
    }
    @Override
    public int getFactoryId() {
        return IdentifiedFactory.FACTORY_ID;
    }
    @Override
    public int getClassId() {
        return CLASS_ID;
    }
    @Override
    public void writeData(ObjectDataOutput out) throws IOException {
        out.writeUTF(value);
    }
    @Override
    public void readData(ObjectDataInput in) throws IOException {
        value = in.readUTF();
    }
    @Override
    public String process(Map.Entry<String, String> entry) {
        entry.setValue(value);
        return value;
    }
}

You can implement the above processor’s factory as follows:

public class IdentifiedFactory implements DataSerializableFactory {
    public static final int FACTORY_ID = 5;
    @Override
    public IdentifiedDataSerializable create(int typeId) {
        if (typeId == IdentifiedEntryProcessor.CLASS_ID) {
            return new IdentifiedEntryProcessor();
        }
        return null;
    }
}

And the following is the configuration for the above factory:

  • XML

  • YAML

<hazelcast>
    <serialization>
        <data-serializable-factories>
            <data-serializable-factory factory-id="5">
                IdentifiedFactory
            </data-serializable-factory>
        </data-serializable-factories>
    </serialization>
</hazelcast>
hazelcast:
  serialization:
    data-serializable-factories:
      - factory-id: 5
        class-name: IdentifiedFactory

Then, you can start your Hazelcast member by using the start scripts (start.sh or start.bat) in the /bin directory. The start scripts automatically adds your class and JAR files to the classpath.