Changing Code without a Restart

The @SupportCodeChange annotation is used to tell the Space that your code has changed during runtime, affecting the Space task that needs to be executed. The Space can store multiple versions of the same task. This is useful in supporting client applications that may need to implement multiple different versions of a task.

This annotation can be used with:

Task Execution

The annotation can be used when defining task execution. For example, annotate your task with @SupportCodeChange(id="1"). 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());

For detailed information about task execution, see the Task Execution topic.

Custom Change

The annotation can be used for custom change operations, as demonstrated in the example below.

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);

For detailed information about the Custom Change operation, see the Custom Change topic.

Custom Aggregation

The annotation can also be used for custom aggregation operations, as demonstrated by the following example.

@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)");

For detailed information about how to use custom aggregators, see the Aggregators topic.

Number of Caches

The default limit of class loaders (caches) is 3. when this limit is breached, the oldest cache is evicted in favor of the new one. This value can be modified via the following 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 annotations without an 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.