Deploying User Code from Clients
User Code Deployment has been deprecated and will be removed in the next major version. To continue deploying your user code after this time, Community Edition users can either upgrade to Enterprise Edition, or add their resources to the Hazelcast member class paths. Hazelcast recommends that Enterprise Edition users migrate their user code to use User Code Namespaces for all purposes other than Jet stream processing. For further information on migrating from User Code Deployment to User Code Namespaces, see Migrate from User Code Deployment. |
You can also deploy your code from the client side for the following situations:
-
You have objects that run on the cluster via the clients such as
Runnable
,Callable
andEntryProcessor
. -
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 using the generic object interface (GenericRecord ). See Accessing Domain Objects Without Domain Classes.
|
Following are example configuration snippets:
Declarative Configuration:
In your hazelcast-client.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
To enable all existing and new members in a cluster to receive classes from clients, make sure that all member configurations follow these rules:
-
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 toLOCAL_AND_CACHED_CLASSES
on all members. -
No
provider-filter
must be configured.
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 about 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 /bin/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.writeString(value);
}
@Override
public void readData(ObjectDataInput in) throws IOException {
value = in.readString();
}
@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:
<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.