Change code without restarts


When executing user code on the space (e.g. space tasks), the space automatically loads the code from the remote client and caches it for future executions. Since the code is cached, modifications are ignored, and users are forced to restart the space whenever they modify the code.

Starting with 12.1, you can use the @SupportCodeChange annotation to tell the space your code has changed. The space can store multiple versions of the same task. This is ideal for supporting clients using different versions of a task.

This annotation can be used with:

Task execution

For example, start with annotating your task with @SupportCodeChange(id=“1”), and when the code changes, set the annotation to @SupportCodeChange(id=“2”), and the space will load the new task.

import org.openspaces.core.executor.Task;

import com.gigaspaces.annotation.SupportCodeChange;

@SupportCodeChange(id="1")
public class DynamicTask implements Task<Integer> {

    @Override
    public Integer execute() throws Exception {
        return new Integer(1);
    }
}
import org.openspaces.core.executor.Task;

import com.gigaspaces.annotation.SupportCodeChange;

@SupportCodeChange(id="2")
public class DynamicTask implements Task<Integer> {

    @Override
    public Integer execute() throws Exception {
        return new Integer(2);
    }
}
 GigaSpace gigaSpace = new GigaSpaceConfigurer(new SpaceProxyConfigurer("xapSpace")).gigaSpace();

 DynamicTask task = new DynamicTask();

 AsyncFuture<Integer> execute = gigaSpace.execute(task);
 System.out.println(execute.get());
See also:

For detailed information on task execution see Task Execution over the space

Custom Change

The annotation can be used for custom change operations.

import com.gigaspaces.annotation.SupportCodeChange;
import com.gigaspaces.client.CustomChangeOperation;
import com.gigaspaces.server.MutableServerEntry;

@SupportCodeChange(id="1")
public class MultiplyIntegerChangeOperation extends CustomChangeOperation {
    private static final long serialVersionUID = 1L;
    private final String path;
    private final int multiplier;

    public MultiplyIntegerChangeOperation(String path) {
        this.path = path;
    }

    @Override
    public String getName() {
        return "multiplyInt";
    }

    public String getPath() {
        return path;
    }

    @Override
    public Object change(MutableServerEntry entry) {
        int oldValue = (Integer) entry.getPathValue(path);
        int newValue = oldValue * 10;
        entry.setPathValue(path, newValue);
        return newValue;
    }
}
import com.gigaspaces.annotation.SupportCodeChange;
import com.gigaspaces.client.CustomChangeOperation;
import com.gigaspaces.server.MutableServerEntry;

@SupportCodeChange(id="2")
public class MultiplyIntegerChangeOperation extends CustomChangeOperation {
    private static final long serialVersionUID = 1L;
    private final String path;

    public MultiplyIntegerChangeOperation(String path) {
        this.path = path;
    }

    @Override
    public String getName() {
        return "multiplyInt";
    }

    public String getPath() {
        return path;
    }

    @Override
    public Object change(MutableServerEntry entry) {
        int oldValue = (Integer) entry.getPathValue(path);
        int newValue = oldValue * 20;
        entry.setPathValue(path, newValue);
        return newValue;
    }
}

    GigaSpace gigaSpace = new GigaSpaceConfigurer(new SpaceProxyConfigurer("xapSpace")).gigaSpace();

    SQLQuery<Employee> query = new SQLQuery<Employee>(Employee.class, "salary > 50");
    ChangeResult<Employee> result = gigaSpace.change(query,
            new ChangeSet().custom(new MultiplyIntegerChangeOperation("salary")),
            ChangeModifiers.RETURN_DETAILED_RESULTS);
See also:

For detailed information on Custom Change see Custom Change

Custom Aggregation

The annotation can be used for custom aggregation operations.

@SupportCodeChange(id ="1")
public class ConcatAggregator extends SpaceEntriesAggregator<String> {

    private final String path;
    private transient StringBuilder sb;

    public ConcatAggregator(String path) {
        this.path = path;
    }

    @Override
    public String getDefaultAlias() {
        return "concat(" + path + ")";
    }

    @Override
    public void aggregate(SpaceEntriesAggregatorContext context) {
        String value = (String) context.getPathValue(path);
        if (value != null)
            concat(value);
    }

    @Override
    public String getIntermediateResult() {
        return sb == null ? null : sb.toString();
    }

    @Override
    public void aggregateIntermediateResult(String partitionResult) {
        concat(partitionResult);
    }

    private void concat(String s) {
        if (sb == null) {
            sb = new StringBuilder(s);
        } else {
            sb.append(',').append(s);
        }
    }
}
@SupportCodeChange(id ="2")
public class ConcatAggregator extends SpaceEntriesAggregator<String> {

    private final String path;
    private transient StringBuilder sb;

    public ConcatAggregator(String path) {
        this.path = path;
    }

    @Override
    public String getDefaultAlias() {
        return "concat(" + path + ")";
    }

    @Override
    public void aggregate(SpaceEntriesAggregatorContext context) {
        String value = (String) context.getPathValue(path);
        if (value != null)
            concat(value);
    }

    @Override
    public String getIntermediateResult() {
        return sb == null ? null : sb.toString();
    }

    @Override
    public void aggregateIntermediateResult(String partitionResult) {
        concat(partitionResult);
    }

    private void concat(String s) {
        if (sb == null) {
            sb = new StringBuilder(s);
        } else {
            sb.append(':').append(s);
        }
    }
}
 GigaSpace gigaSpace = new GigaSpaceConfigurer(new SpaceProxyConfigurer("xapSpace")).gigaSpace();
    SQLQuery<Employee> query = new SQLQuery<Employee>(Employee.class, "salary > 50");
    AggregationResult result = gigaSpace.aggregate(query, new AggregationSet().add(new ConcatAggregator("name")));
    String concatResult = result.getString("concat(name)");

See also:

For detailed information on Custom Aggregator see Aggregators

Number of caches

The default limit of class loaders (caches) is set to 3, when breached, the oldest cache is evicted in favor of the new one. This can be modified via space properties:

Property name Description Default
space-config.remote-code.max-class-loaders Limit number of class loaders (caches) 3
space-config.remote-code.support.code.change Enable / Disable code change true

@SupportCodeChange without id or with id=“” are not cached.

Limitations

  • When using @SupportCodeChange no new types, can be introduced to the space.
  • Anonymous classes and lambdas do not support the annotation.