/* * 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.cache.impl.operation; import com.hazelcast.cache.impl.CacheDataSerializerHook; import com.hazelcast.cache.impl.ICacheService; import com.hazelcast.config.CacheConfig; import com.hazelcast.core.ExecutionCallback; import com.hazelcast.core.Member; import com.hazelcast.nio.ObjectDataInput; import com.hazelcast.nio.ObjectDataOutput; import com.hazelcast.nio.serialization.IdentifiedDataSerializable; import com.hazelcast.spi.NodeEngine; import com.hazelcast.spi.OperationService; import com.hazelcast.spi.impl.AbstractNamedOperation; import com.hazelcast.spi.impl.SimpleExecutionCallback; import java.io.IOException; import java.util.Collection; import java.util.concurrent.atomic.AtomicInteger; /** * Used to create cluster wide cache configuration. * <p>This configuration is created using the following algorithm; * <ul> * <li>Find partition id using the distributed object name of cache as a key.</li> * <li>Send the <code>CacheCreateConfigOperation</code> operation to the calculated partition which will force all * clusters to be single threaded.</li> * <li>{@link ICacheService#putCacheConfigIfAbsent(com.hazelcast.config.CacheConfig)} is called.</li> * </ul></p> * <p>This operation's purpose is to pass the required parameters into * {@link ICacheService#putCacheConfigIfAbsent(com.hazelcast.config.CacheConfig)}.</p> */ public class CacheCreateConfigOperation extends AbstractNamedOperation implements IdentifiedDataSerializable { private CacheConfig config; private boolean createAlsoOnOthers = true; private boolean ignoreLocal; private boolean returnsResponse = true; private transient Object response; public CacheCreateConfigOperation() { } public CacheCreateConfigOperation(CacheConfig config) { this(config, true); } public CacheCreateConfigOperation(CacheConfig config, boolean createAlsoOnOthers) { this(config, createAlsoOnOthers, false); } public CacheCreateConfigOperation(CacheConfig config, boolean createAlsoOnOthers, boolean ignoreLocal) { super(config.getNameWithPrefix()); this.config = config; this.createAlsoOnOthers = createAlsoOnOthers; this.ignoreLocal = ignoreLocal; } @Override public String getServiceName() { return ICacheService.SERVICE_NAME; } @Override public void run() throws Exception { ICacheService service = getService(); if (!ignoreLocal) { response = service.putCacheConfigIfAbsent(config); } if (createAlsoOnOthers) { NodeEngine nodeEngine = getNodeEngine(); Collection<Member> members = nodeEngine.getClusterService().getMembers(); int remoteNodeCount = members.size() - 1; if (remoteNodeCount > 0) { postponeReturnResponse(); ExecutionCallback<Object> callback = new CacheConfigCreateCallback(this, remoteNodeCount); OperationService operationService = nodeEngine.getOperationService(); for (Member member : members) { if (!member.localMember()) { CacheCreateConfigOperation op = new CacheCreateConfigOperation(config, false); operationService .createInvocationBuilder(ICacheService.SERVICE_NAME, op, member.getAddress()) .setExecutionCallback(callback) .invoke(); } } } } } private void postponeReturnResponse() { // If config already exists or it's local-only created then return response immediately. // Otherwise response will be sent after config is created on all members. returnsResponse = false; } @Override public void onExecutionFailure(Throwable e) { // Execution failed so we should enable `returnsResponse` flag to prevent waiting anymore returnsResponse = true; super.onExecutionFailure(e); } private static class CacheConfigCreateCallback extends SimpleExecutionCallback<Object> { final AtomicInteger counter; final CacheCreateConfigOperation operation; public CacheConfigCreateCallback(CacheCreateConfigOperation op, int count) { this.operation = op; this.counter = new AtomicInteger(count); } @Override public void notify(Object object) { if (counter.decrementAndGet() == 0) { operation.sendResponse(null); } } } @Override public Object getResponse() { return response; } @Override public boolean returnsResponse() { return returnsResponse; } @Override protected void writeInternal(ObjectDataOutput out) throws IOException { super.writeInternal(out); out.writeObject(config); out.writeBoolean(createAlsoOnOthers); out.writeBoolean(ignoreLocal); } @Override protected void readInternal(ObjectDataInput in) throws IOException { super.readInternal(in); config = in.readObject(); createAlsoOnOthers = in.readBoolean(); ignoreLocal = in.readBoolean(); } @Override public int getId() { return CacheDataSerializerHook.CREATE_CONFIG; } @Override public int getFactoryId() { return CacheDataSerializerHook.F_ID; } }