blob: 0398b93a3f8d0bbdaaf11113b4a2a7c7440f2fe5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.internal.cache.execute;
import static org.apache.geode.cache.RegionShortcut.PARTITION;
import static org.apache.geode.distributed.ConfigurationProperties.SERIALIZABLE_OBJECT_FILTER;
import static org.apache.geode.test.dunit.Host.getHost;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Collection;
import java.util.Properties;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.apache.geode.cache.PartitionAttributesFactory;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionFactory;
import org.apache.geode.cache.execute.Execution;
import org.apache.geode.cache.execute.FunctionService;
import org.apache.geode.cache.execute.ResultCollector;
import org.apache.geode.internal.cache.PartitionedRegion;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.dunit.cache.CacheTestCase;
import org.apache.geode.test.junit.categories.FunctionServiceTest;
/**
* TRAC #40714: Registering a function on a Java client changes the behavior when executing an
* instance of the function
*
* On a Java client if a function is registered with id="f1":
*
* <pre>
* FunctionService.registerFunction(new FunctionAdapter() {
* private final Integer state = 1;
*
* public String getId() {
* return "f1";
* }
*
* public void execute(FunctionContext fc) {}
* });
* </pre>
*
* But execute a particular instance of a function e.g.:
*
* <pre>
* FunctionService.onRegion(r).execute(new FunctionAdapter() {
* private final Integer state = 2;
*
* public String getId() {
* return "f1";
* }
*
* public void execute(FunctionContext fc) {}
* });
* </pre>
*
* The execution code does not serialize (nor execute) the instance which was given. The problems
* are:
* <ul>
*
* <li>Confusing behavior, the programmer intended an instance (and its state) to be serialized and
* executed.</li>
*
* <li>Assumes that a function with the same Id is registered on the server which is not necessarily
* the case.</li>
*
* </ul>
*
* <p>
* Extracted from {@link PRFunctionExecutionDUnitTest}.
*/
@Category({FunctionServiceTest.class})
public class ExecuteFunctionInstanceRegressionTest extends CacheTestCase {
private String regionName;
private VM datastore0;
private VM datastore1;
private VM datastore2;
private VM datastore3;
@Before
public void setUp() {
datastore0 = getHost(0).getVM(0);
datastore1 = getHost(0).getVM(1);
datastore2 = getHost(0).getVM(2);
datastore3 = getHost(0).getVM(3);
regionName = getUniqueName();
getCache();
}
@Override
public Properties getDistributedSystemProperties() {
Properties config = new Properties();
config.put(SERIALIZABLE_OBJECT_FILTER,
"org.apache.geode.test.junit.rules.**;org.apache.geode.internal.cache.execute.**;org.apache.geode.internal.cache.functions.**;org.apache.geode.test.dunit.**");
return config;
}
@Test
public void providedFunctionInstanceShouldBeUsed() throws Exception {
datastore0.invoke(() -> createPartitionedRegion());
datastore1.invoke(() -> createPartitionedRegion());
datastore2.invoke(() -> createPartitionedRegion());
datastore3.invoke(() -> createPartitionedRegion());
datastore0.invoke(() -> FunctionService.registerFunction(new BooleanFunction(false)));
datastore1.invoke(() -> FunctionService.registerFunction(new BooleanFunction(false)));
datastore2.invoke(() -> FunctionService.registerFunction(new BooleanFunction(false)));
datastore3.invoke(() -> FunctionService.registerFunction(new BooleanFunction(false)));
datastore3.invoke(() -> {
PartitionedRegion pr = (PartitionedRegion) getCache().getRegion(regionName);
createAllBuckets(pr);
ResultCollector<Boolean, Collection<Boolean>> rc =
executeFunctionWithId(pr, BooleanFunction.class.getName());
validateResults(rc, false);
});
datastore3.invoke(() -> {
PartitionedRegion pr = (PartitionedRegion) getCache().getRegion(regionName);
ResultCollector<Boolean, Collection<Boolean>> rc =
executeFunctionWithInstance(pr, new BooleanFunction(true));
validateResults(rc, true);
});
}
private void createPartitionedRegion() {
PartitionAttributesFactory paf = new PartitionAttributesFactory();
paf.setLocalMaxMemory(10);
paf.setRedundantCopies(0);
paf.setTotalNumBuckets(17);
RegionFactory<Integer, Integer> regionFactory = getCache().createRegionFactory(PARTITION);
regionFactory.setPartitionAttributes(paf.create());
regionFactory.create(regionName);
}
private void createAllBuckets(final PartitionedRegion pr) {
for (int bucketId = 0; bucketId < pr.getTotalNumberOfBuckets(); bucketId++) {
pr.put(bucketId, bucketId);
}
for (int bucketId = 0; bucketId < pr.getTotalNumberOfBuckets(); bucketId++) {
assertThat(pr.getBucketKeys(bucketId)).hasSize(1);
}
}
private ResultCollector<Boolean, Collection<Boolean>> executeFunctionWithInstance(
final Region region, final BooleanFunction function) {
Execution<Void, Boolean, Collection<Boolean>> dataSet = FunctionService.onRegion(region);
ResultCollector<Boolean, Collection<Boolean>> resultCollector = dataSet.execute(function);
return resultCollector;
}
private ResultCollector executeFunctionWithId(final Region region, final String functionId) {
Execution<Void, Boolean, Collection<Boolean>> dataSet = FunctionService.onRegion(region);
ResultCollector<Boolean, Collection<Boolean>> resultCollector = dataSet.execute(functionId);
return resultCollector;
}
private void validateResults(final ResultCollector<Boolean, Collection<Boolean>> resultCollector,
final boolean expectedResult) {
Collection<Boolean> results = resultCollector.getResult();
assertThat(results).hasSize(4).containsExactly(expectedResult, expectedResult, expectedResult,
expectedResult);
}
}