All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.internal.cache.execute.AbstractExecution Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2010-2015 Pivotal Software, 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. See accompanying
 * LICENSE file.
 */

package com.gemstone.gemfire.internal.cache.execute;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.gemstone.gemfire.InternalGemFireException;
import com.gemstone.gemfire.SystemFailure;
import com.gemstone.gemfire.cache.client.internal.ProxyCache;
import com.gemstone.gemfire.cache.LowMemoryException;
import com.gemstone.gemfire.cache.TransactionException;
import com.gemstone.gemfire.cache.execute.Function;
import com.gemstone.gemfire.cache.execute.FunctionContext;
import com.gemstone.gemfire.cache.execute.FunctionException;
import com.gemstone.gemfire.cache.execute.FunctionInvocationTargetException;
import com.gemstone.gemfire.cache.execute.FunctionService;
import com.gemstone.gemfire.cache.execute.ResultCollector;
import com.gemstone.gemfire.distributed.internal.DM;
import com.gemstone.gemfire.distributed.internal.DistributionManager;
import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem;
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.cache.TXStateInterface;
import com.gemstone.gemfire.internal.cache.tier.sockets.ServerConnection;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;

/**
 * Abstract implementation of InternalExecution interface.
 *  
 * @author Yogesh Mahajan
 * @since 5.8LA
 *
 */
public abstract class AbstractExecution implements InternalExecution {

  protected boolean isMemberMappedArgument;

  protected MemberMappedArgument memberMappedArg;

  protected Object args;

  protected ResultCollector rc;

  protected Set filter = new HashSet();

  protected boolean hasRoutingObjects;
  
  protected volatile boolean isReExecute = false;
  
  protected volatile boolean isClientServerMode = false;
  
  protected Set failedNodes = new HashSet();
  
  protected boolean isFnSerializationReqd;

  /***
   * yjing The following code is added to get a set of function executing nodes
   * by the data aware procedure
   */
  protected Collection executionNodes = null;

  public static interface ExecutionNodesListener {

    public void afterExecutionNodesSet(AbstractExecution execution);

    public void reset();
  }

  protected ExecutionNodesListener executionNodesListener = null;

  protected boolean waitOnException = false;
  
  protected boolean forwardExceptions = false;

  protected boolean ignoreDepartedMembers = false;

  protected ProxyCache proxyCache;
  
  private final static ConcurrentHashMap idToFunctionAttributes = new ConcurrentHashMap();
  
  public static final byte NO_HA_NO_HASRESULT_NO_OPTIMIZEFORWRITE = 0;

  public static final byte NO_HA_HASRESULT_NO_OPTIMIZEFORWRITE = 2;

  public static final byte HA_HASRESULT_NO_OPTIMIZEFORWRITE = 3;

  public static final byte NO_HA_NO_HASRESULT_OPTIMIZEFORWRITE = 4;

  public static final byte NO_HA_HASRESULT_OPTIMIZEFORWRITE = 6;

  public static final byte HA_HASRESULT_OPTIMIZEFORWRITE = 7;
  
  public static final byte HA_HASRESULT_NO_OPTIMIZEFORWRITE_REEXECUTE = 11;
  
  public static final byte HA_HASRESULT_OPTIMIZEFORWRITE_REEXECUTE = 15;

  public static final byte getFunctionState(boolean isHA, boolean hasResult,
      boolean optimizeForWrite) {
    if (isHA) {
      if (hasResult) {
        if (optimizeForWrite) {
          return HA_HASRESULT_OPTIMIZEFORWRITE;
        }
        else {
          return HA_HASRESULT_NO_OPTIMIZEFORWRITE;
        }
      }
      return (byte)1; // ERROR scenario
    }
    else {
      if (hasResult) {
        if (optimizeForWrite) {
          return NO_HA_HASRESULT_OPTIMIZEFORWRITE;
        }
        else {
          return NO_HA_HASRESULT_NO_OPTIMIZEFORWRITE;
        }
      }
      else {
        if (optimizeForWrite) {
          return NO_HA_NO_HASRESULT_OPTIMIZEFORWRITE;
        }
        else {
          return NO_HA_NO_HASRESULT_NO_OPTIMIZEFORWRITE;
        }
      }
    }
  }
  
  public static final byte getReexecuteFunctionState(byte fnState) {
    if (fnState == HA_HASRESULT_NO_OPTIMIZEFORWRITE) {
      return HA_HASRESULT_NO_OPTIMIZEFORWRITE_REEXECUTE;
    } else if (fnState == HA_HASRESULT_OPTIMIZEFORWRITE) {
      return HA_HASRESULT_OPTIMIZEFORWRITE_REEXECUTE;
    }
    throw new InternalGemFireException("Wrong fnState provided.");
  }
  
  protected AbstractExecution() {
    this.hasRoutingObjects = false;
  }

  protected AbstractExecution(AbstractExecution ae) {
    if (ae.args != null) {
      this.args = ae.args;
    }
    if (ae.rc != null) {
      this.rc = ae.rc;
    }
    if (ae.memberMappedArg != null) {
      this.memberMappedArg = ae.memberMappedArg;
    }
    this.isMemberMappedArgument = ae.isMemberMappedArgument;
    this.hasRoutingObjects = ae.hasRoutingObjects;
    this.isClientServerMode = ae.isClientServerMode;
    if (ae.proxyCache != null) {
      this.proxyCache = ae.proxyCache;
    }
    this.isFnSerializationReqd = ae.isFnSerializationReqd;
    this.waitOnException = ae.waitOnException;
  }
  
  protected AbstractExecution(AbstractExecution ae, boolean isReExecute) {
    this(ae);
    this.isReExecute = isReExecute;
  }

  public boolean isMemberMappedArgument() {
    return this.isMemberMappedArgument;
  }

  public Object getArgumentsForMember(String memberId) {
    if (!isMemberMappedArgument) {
      return this.args;
    }
    else {
      return this.memberMappedArg.getArgumentsForMember(memberId);
    }
  }

  public MemberMappedArgument getMemberMappedArgument() {
    return this.memberMappedArg;
  }

  public Object getArguments() {
    return this.args;
  }

  public ResultCollector getResultCollector() {
    return this.rc;
  }

  public AbstractExecution withRoutingObjects(Set routingObjects) {
    if (routingObjects == null) {
      throw new FunctionException(
          LocalizedStrings.FunctionService_ROUTING_OBJECTS_SET_IS_NULL
              .toLocalizedString());
    }
    this.filter.clear();
    this.filter.addAll(routingObjects);
    this.hasRoutingObjects = true;
    return this;
  }

  public Set getFilter() {
    return this.filter;
  }

  public boolean hasRoutingObjects() {
    return this.hasRoutingObjects;
  }

  public AbstractExecution setIsReExecute() {
    this.isReExecute = true;
    if (this.executionNodesListener != null) {
      this.executionNodesListener.reset();
    }
    return this;
  }

  public boolean isReExecute() {
    return isReExecute;
  }

  public Set getFailedNodes() {
    return this.failedNodes;
  }

  public void addFailedNode(String failedNode) {
    this.failedNodes.add(failedNode);
  }  
  public void clearFailedNodes() {
    this.failedNodes.clear();
  }  

  public boolean isClientServerMode() {
    return isClientServerMode;
  }

  public boolean isFnSerializationReqd() {
    return isFnSerializationReqd;
  }

  public final Collection getExecutionNodes() {
    return this.executionNodes;
  }

  public final void setRequireExecutionNodes(ExecutionNodesListener listener) {
    this.executionNodes = Collections.emptySet();
    this.executionNodesListener = listener;
  }

  public final void setExecutionNodes(Set nodes) {
    if (this.executionNodes != null) {
      this.executionNodes = nodes;
      if (this.executionNodesListener != null) {
        this.executionNodesListener.afterExecutionNodesSet(this);
      }
    }
  }

  public final void executeFunctionOnLocalPRNode(final Function fn,
      final FunctionContext cx,
      final PartitionedRegionFunctionResultSender sender, DM dm,
      final TXStateInterface tx) {
    if (dm instanceof DistributionManager && tx == null) {
      if(ServerConnection.isExecuteFunctionOnLocalNodeOnly().byteValue() == 1) {
        ServerConnection.executeFunctionOnLocalNodeOnly((byte)3);//executed locally
        executeFunctionLocally(fn, cx, sender, dm);
        if (!sender.isLastResultReceived() && fn.hasResult()) {
          ((InternalResultSender)sender)
          .setException(new FunctionException(
                  LocalizedStrings.ExecuteFunction_THE_FUNCTION_0_DID_NOT_SENT_LAST_RESULT
                      .toString(fn.getId())));
        }
      } else {
        
        final DistributionManager newDM = (DistributionManager)dm;
        newDM.getFunctionExcecutor().execute(new Runnable() {
          public void run() {
            executeFunctionLocally(fn, cx, sender, newDM);
            if (!sender.isLastResultReceived() && fn.hasResult()) {
              ((InternalResultSender)sender)
              .setException(new FunctionException(
                      LocalizedStrings.ExecuteFunction_THE_FUNCTION_0_DID_NOT_SENT_LAST_RESULT
                          .toString(fn.getId())));
            }
          }
        });
      }
    }
    else {
      executeFunctionLocally(fn, cx, sender, dm);
      if (!sender.isLastResultReceived() && fn.hasResult()) {
        sender.setException(new FunctionException(LocalizedStrings
            .ExecuteFunction_THE_FUNCTION_0_DID_NOT_SENT_LAST_RESULT
                .toLocalizedString(fn.getId())));
      }
    }
  }

  // Bug41118 : in case of lonerDistribuedSystem do local execution through
  // main thread otherwise give execution to FunctionExecutor from
  // DistributionManager
  public final void executeFunctionOnLocalNode(final Function fn,
      final FunctionContext cx, final InternalResultSender sender,
      DM dm, final TXStateInterface tx) {
    if (dm instanceof DistributionManager && tx == null) {
      final DistributionManager newDM = (DistributionManager)dm;
      newDM.getFunctionExcecutor().execute(new Runnable() {
        public void run() {
          executeFunctionLocally(fn, cx, sender, newDM);
          if (!sender.isLastResultReceived() && fn.hasResult()) {
            sender.setException(new FunctionException(LocalizedStrings
                .ExecuteFunction_THE_FUNCTION_0_DID_NOT_SENT_LAST_RESULT
                    .toLocalizedString(fn.getId())));
          }
        }
      });
    }
    else {
      executeFunctionLocally(fn, cx, sender, dm);
      if (!sender.isLastResultReceived() && fn.hasResult()) {
        sender.setException(new FunctionException(LocalizedStrings
            .ExecuteFunction_THE_FUNCTION_0_DID_NOT_SENT_LAST_RESULT
                .toLocalizedString(fn.getId())));
      }
    }
  }

  public final void executeFunctionLocally(final Function fn,
      final FunctionContext cx, final InternalResultSender sender, final DM dm) {

    FunctionStats stats = FunctionStats.getFunctionStats(fn.getId(), dm
        .getSystem());
    LogWriterI18n logger = dm.getLoggerI18n(); 
    try {
      long start = stats.startTime();
      stats.startFunctionExecution(fn.hasResult());
      if (logger.fineEnabled()) {
        logger.fine("Executing Function: " + fn.getId()
            + " on local node with context: " + cx.toString());
      }
      fn.execute(cx);
      stats.endFunctionExecution(start, fn.hasResult());
    }
    catch (FunctionInvocationTargetException fite) {
      FunctionException functionException = null;
      if (fn.isHA()) {
        functionException = new FunctionException(
            new InternalFunctionInvocationTargetException(fite.getMessage()));
      }
      else {
        functionException = new FunctionException(fite);
      }
      handleException(functionException, fn, cx, sender, dm);
    }
    catch (BucketMovedException bme) {
      FunctionException functionException = null;
      if (fn.isHA()) {
        functionException = new FunctionException(
            new InternalFunctionInvocationTargetException(bme));
      }
      else {
        functionException = new FunctionException(bme);
      }
      handleException(functionException, fn, cx, sender, dm);
    }
    catch (VirtualMachineError e) {
      SystemFailure.initiateFailure(e);
      throw e;
    }
    catch (Throwable t) {
      SystemFailure.checkFailure();
      handleException(t, fn, cx, sender, dm);
    }
  }

  public ResultCollector execute(final String functionName) {
    if (functionName == null) {
      throw new FunctionException(
          LocalizedStrings.ExecuteFunction_THE_INPUT_FUNCTION_FOR_THE_EXECUTE_FUNCTION_REQUEST_IS_NULL
              .toLocalizedString());
    }
    this.isFnSerializationReqd = false;
    Function functionObject = FunctionService.getFunction(functionName);
    if (functionObject == null) {
      throw new FunctionException(
          LocalizedStrings.ExecuteFunction_FUNCTION_NAMED_0_IS_NOT_REGISTERED
              .toLocalizedString(functionName));
    }
    
    return executeFunction(functionObject);
  }

  public ResultCollector execute(Function function) throws FunctionException {
    if (function == null) {
      throw new FunctionException(
          LocalizedStrings.ExecuteFunction_THE_INPUT_FUNCTION_FOR_THE_EXECUTE_FUNCTION_REQUEST_IS_NULL
              .toLocalizedString());
    }
    if (function.isHA() && !function.hasResult()) {
      throw new FunctionException(
          LocalizedStrings.FunctionService_FUNCTION_ATTRIBUTE_MISMATCH
              .toLocalizedString());
    }

    String id = function.getId();
    if (id == null) {
      throw new IllegalArgumentException(
          LocalizedStrings.ExecuteFunction_THE_FUNCTION_GET_ID_RETURNED_NULL
              .toLocalizedString());
    }
    this.isFnSerializationReqd = true;
    return executeFunction(function);
  }

  public final void setWaitOnExceptionFlag(boolean waitOnException) {
    this.waitOnException = waitOnException;
  }

  public boolean getWaitOnExceptionFlag() {
    return this.waitOnException;
  }

  public void setForwardExceptions(boolean forward) {
    this.forwardExceptions = forward;
  }
  
  public boolean isForwardExceptions() {
    return forwardExceptions;
  }

  @Override
  public void setIgnoreDepartedMembers(boolean ignore) {
    this.ignoreDepartedMembers = ignore;
    if (ignore) {
      setWaitOnExceptionFlag(true);
    }
  }

  public boolean isIgnoreDepartedMembers() {
    return this.ignoreDepartedMembers;
  }

  protected abstract ResultCollector executeFunction(Function fn);

  /**
   * validates whether a function should execute in presence of transaction
   * and HeapCritical members. If the function is the first operation in a
   * transaction, bootstraps the function.
   * @param function the function
   * @param targetMembers the set of members the function will be executed on
   * @throws TransactionException if more than one nodes are targeted within a transaction
   * @throws LowMemoryException if the set contains a heap critical member
   */
  public abstract void validateExecution(Function function, Set targetMembers);

  public final LocalResultCollector getLocalResultCollector(
      Function function, final ResultCollector rc) {
    if (rc instanceof LocalResultCollector) {
      return (LocalResultCollector)rc;
    }
    else {
      return new LocalResultCollectorImpl(function, rc, this);
    }
  }
  
  /**
   * Returns the function attributes defined by the functionId, returns null if no
   * function is found for the specified functionId
   * 
   * @param functionId
   * @return byte[]
   * @throws FunctionException
   *                 if functionID passed is null
   * @since 6.6
   */
  public byte[] getFunctionAttributes(String functionId) {
    if (functionId == null) {
      throw new FunctionException(
          LocalizedStrings.FunctionService_0_PASSED_IS_NULL
              .toLocalizedString("functionId instance "));
    }
    return idToFunctionAttributes.get(functionId);
  }
  
  public void removeFunctionAttributes(String functionId) {
    idToFunctionAttributes.remove(functionId);
  }

  public void addFunctionAttributes(String functionId, byte[] functionAttributes) {
    idToFunctionAttributes.put(functionId, functionAttributes);
  }
  
  public ResultCollector execute(String functionName, boolean hasResult)
      throws FunctionException {
    if (functionName == null) {
      throw new FunctionException(
          LocalizedStrings.ExecuteFunction_THE_INPUT_FUNCTION_FOR_THE_EXECUTE_FUNCTION_REQUEST_IS_NULL
              .toLocalizedString());
    }
    Function functionObject = FunctionService.getFunction(functionName);
    if (functionObject == null) {
      throw new FunctionException(
          LocalizedStrings.ExecuteFunction_FUNCTION_NAMED_0_IS_NOT_REGISTERED
              .toLocalizedString(functionObject));
    }
    
    byte registeredFunctionState = AbstractExecution.getFunctionState(
        functionObject.isHA(), functionObject.hasResult(), functionObject
            .optimizeForWrite());

    byte functionState = AbstractExecution.getFunctionState(hasResult, hasResult, false);
    if (registeredFunctionState != functionState) {
      throw new FunctionException(
          LocalizedStrings.FunctionService_FUNCTION_ATTRIBUTE_MISMATCH_CLIENT_SERVER
              .toLocalizedString(functionName));
    }
    
    this.isFnSerializationReqd = false;
    // If hasResult is true, isHA will also be true and hasResult is false then isHA will be false
    // For other combination use next API
    return executeFunction(functionObject);
  }
  
  public ResultCollector execute(String functionName, boolean hasResult, boolean isHA) throws FunctionException {
    if (functionName == null) {
      throw new FunctionException(
          LocalizedStrings.ExecuteFunction_THE_INPUT_FUNCTION_FOR_THE_EXECUTE_FUNCTION_REQUEST_IS_NULL
              .toLocalizedString());
    }
    if (isHA && !hasResult) {
      throw new FunctionException(
          LocalizedStrings.FunctionService_FUNCTION_ATTRIBUTE_MISMATCH
              .toLocalizedString());
    }
    Function functionObject = FunctionService.getFunction(functionName);
    if (functionObject == null) {
      throw new FunctionException(
          LocalizedStrings.ExecuteFunction_FUNCTION_NAMED_0_IS_NOT_REGISTERED
              .toLocalizedString(functionObject));
    }
    byte registeredFunctionState = AbstractExecution.getFunctionState(
        functionObject.isHA(), functionObject.hasResult(), functionObject
            .optimizeForWrite());

    byte functionState = AbstractExecution.getFunctionState(isHA, hasResult, false);
    if (registeredFunctionState != functionState) {
      throw new FunctionException(
          LocalizedStrings.FunctionService_FUNCTION_ATTRIBUTE_MISMATCH_CLIENT_SERVER
              .toLocalizedString(functionName));
    }
    
    this.isFnSerializationReqd = false;
    return executeFunction(functionObject);
  }
  
  public ResultCollector execute(String functionName, boolean hasResult, boolean isHA, boolean isOptimizeForWrite) throws FunctionException {
    if (functionName == null) {
      throw new FunctionException(
          LocalizedStrings.ExecuteFunction_THE_INPUT_FUNCTION_FOR_THE_EXECUTE_FUNCTION_REQUEST_IS_NULL
              .toLocalizedString());
    }
    if (isHA && !hasResult) {
      throw new FunctionException(
          LocalizedStrings.FunctionService_FUNCTION_ATTRIBUTE_MISMATCH
              .toLocalizedString());
    }
    Function functionObject = FunctionService.getFunction(functionName);
    if (functionObject == null) {
      throw new FunctionException(
          LocalizedStrings.ExecuteFunction_FUNCTION_NAMED_0_IS_NOT_REGISTERED
              .toLocalizedString(functionObject));
    }
    byte registeredFunctionState = AbstractExecution.getFunctionState(
        functionObject.isHA(), functionObject.hasResult(), functionObject
            .optimizeForWrite());

    byte functionState = AbstractExecution.getFunctionState(isHA, hasResult, isOptimizeForWrite);
    if (registeredFunctionState != functionState) {
      throw new FunctionException(
          LocalizedStrings.FunctionService_FUNCTION_ATTRIBUTE_MISMATCH_CLIENT_SERVER
              .toLocalizedString(functionName));
    }
    
    this.isFnSerializationReqd = false;
    return executeFunction(functionObject);
  }

  private void handleException(Throwable functionException, final Function fn,
      final FunctionContext cx, final InternalResultSender sender, DM dm) {
    FunctionStats stats = FunctionStats.getFunctionStats(fn.getId(), dm
        .getSystem());
    LogWriterI18n logger = dm.getLoggerI18n(); 
    
    if (logger.fineEnabled()) {
      logger.fine(
          "Exception occured on local node while executing Function: "
              + fn.getId(), functionException);
    }
    stats.endFunctionExecutionWithException(fn.hasResult());
    if (fn.hasResult()) {
      if (waitOnException || forwardExceptions) {
        sender.lastResult(functionException);
      } else {
        sender.setException(functionException);
      }
    } else {
      if (logger.warningEnabled()) {
        logger.warning(
            LocalizedStrings.FunctionService_EXCEPTION_ON_LOCAL_NODE,
            functionException);
      }
    }
  }
}