/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * Licensed 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 com.hazelcast.internal.partition.service.fragment; import com.hazelcast.config.ServiceConfig; import com.hazelcast.internal.partition.service.TestAbstractMigrationAwareService; import com.hazelcast.spi.FragmentedMigrationAwareService; import com.hazelcast.spi.Operation; import com.hazelcast.spi.PartitionMigrationEvent; import com.hazelcast.spi.PartitionReplicationEvent; import com.hazelcast.spi.ServiceNamespace; import com.hazelcast.spi.partition.MigrationEndpoint; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static org.hamcrest.Matchers.isIn; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThat; public class TestFragmentedMigrationAwareService extends TestAbstractMigrationAwareService<String> implements FragmentedMigrationAwareService { public static final String SERVICE_NAME = TestFragmentedMigrationAwareService.class.getSimpleName(); public static ServiceConfig createServiceConfig(int backupCount) { return new ServiceConfig() .setEnabled(true).setName(TestFragmentedMigrationAwareService.SERVICE_NAME) .setClassName(TestFragmentedMigrationAwareService.class.getName()) .addProperty(BACKUP_COUNT_PROP, String.valueOf(backupCount)); } private final ConcurrentMap<Key, Integer> data = new ConcurrentHashMap<Key, Integer>(); int inc(String name, int partitionId) { Key key = new Key(name, partitionId); Integer count = data.get(key); if (count == null) { count = 1; } else { count++; } data.put(key, count); return count; } void put(String name, int partitionId, int value) { data.put(new Key(name, partitionId), value); } @Override public Collection<ServiceNamespace> getAllServiceNamespaces(PartitionReplicationEvent event) { if (event.getReplicaIndex() > backupCount) { return Collections.emptySet(); } Set<ServiceNamespace> knownNamespaces = new HashSet<ServiceNamespace>(); for (Key key : data.keySet()) { knownNamespaces.add(new TestServiceNamespace(key.name)); } return Collections.unmodifiableCollection(knownNamespaces); } @Override public boolean isKnownServiceNamespace(ServiceNamespace namespace) { return namespace instanceof TestServiceNamespace; } @Override public Operation prepareReplicationOperation(PartitionReplicationEvent event, Collection<ServiceNamespace> namespaces) { if (event.getReplicaIndex() > backupCount || namespaces.isEmpty()) { return null; } Collection<ServiceNamespace> knownNamespaces = getAllServiceNamespaces(event); Map<TestServiceNamespace, Integer> values = new HashMap<TestServiceNamespace, Integer>(namespaces.size()); for (ServiceNamespace ns : namespaces) { assertThat(ns, isIn(knownNamespaces)); TestServiceNamespace testNs = (TestServiceNamespace) ns; Integer value = get(testNs.name, event.getPartitionId()); if (value != null) { values.put(testNs, value); } } return values.isEmpty() ? null : new TestFragmentReplicationOperation(values); } @Override protected void onCommitMigration(PartitionMigrationEvent event) { if (event.getMigrationEndpoint() == MigrationEndpoint.SOURCE) { if (event.getNewReplicaIndex() == -1 || event.getNewReplicaIndex() > backupCount) { removePartitionData(event.getPartitionId()); } } if (event.getMigrationEndpoint() == MigrationEndpoint.DESTINATION) { if (event.getNewReplicaIndex() > backupCount) { for (Key key : data.keySet()) { assertNotEquals(event.getPartitionId(), key.partitionId); } } } } @Override protected void onRollbackMigration(PartitionMigrationEvent event) { if (event.getMigrationEndpoint() == MigrationEndpoint.DESTINATION) { if (event.getCurrentReplicaIndex() == -1 || event.getCurrentReplicaIndex() > backupCount) { removePartitionData(event.getPartitionId()); } } } private void removePartitionData(int partitionId) { Iterator<Key> iter = data.keySet().iterator(); while (iter.hasNext()) { Key key = iter.next(); if (key.partitionId == partitionId) { iter.remove(); } } } @Override public int size(String name) { int k = 0; for (Key key : data.keySet()) { if (key.name.equals(name)) { k++; } } return k; } @Override public Integer get(String name, int id) { return data.get(new Key(name, id)); } @Override public boolean contains(String name, int id) { return data.containsKey(new Key(name, id)); } @Override public Collection<Integer> keys(String name) { Set<Integer> set = new HashSet<Integer>(); for (Key key : data.keySet()) { if (key.name.equals(name)) { set.add(key.partitionId); } } return set; } @Override public String getServiceName() { return SERVICE_NAME; } @Override public ServiceNamespace getNamespace(String name) { return new TestServiceNamespace(name); } @Override public Operation prepareReplicationOperation(PartitionReplicationEvent event) { return prepareReplicationOperation(event, getAllServiceNamespaces(event)); } private static class Key { final String name; final int partitionId; Key(String name, int partitionId) { this.name = name; this.partitionId = partitionId; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Key key = (Key) o; return partitionId == key.partitionId && name.equals(key.name); } @Override public int hashCode() { int result = name.hashCode(); result = 31 * result + partitionId; return result; } } }