/*
* 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.ignite.compute.gridify.aop;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteCompute;
import org.apache.ignite.compute.gridify.GridifyInput;
import org.apache.ignite.compute.gridify.GridifyNodeFilter;
import org.apache.ignite.compute.gridify.GridifySetToSet;
import org.apache.ignite.internal.compute.ComputeTaskTimeoutCheckedException;
import org.apache.ignite.internal.util.gridify.GridifyRangeArgument;
import org.apache.ignite.internal.util.gridify.GridifyUtils;
import org.apache.ignite.internal.util.typedef.internal.U;
import static org.apache.ignite.internal.util.gridify.GridifyUtils.UNKNOWN_SIZE;
/**
* Convenience adapter with common methods for different aspect implementations
* (AspectJ, JBoss AOP, Spring AOP).
* This adapter used in grid task for {@link GridifySetToSet} annotation.
*/
public class GridifySetToSetAbstractAspect {
/**
* Check method signature.
*
* @param mtd Grid-enabled method.
* @throws IgniteCheckedException If method signature invalid..
*/
protected void checkMethodSignature(Method mtd) throws IgniteCheckedException {
Class<?>[] paramTypes = mtd.getParameterTypes();
Collection<Integer> allowedParamIdxs = new LinkedList<>();
for (int i = 0; i < paramTypes.length; i++) {
Class<?> paramType = paramTypes[i];
if (GridifyUtils.isMethodParameterTypeAllowed(paramType))
allowedParamIdxs.add(i);
}
if (allowedParamIdxs.isEmpty()) {
throw new IgniteCheckedException("Invalid method signature. Failed to get valid method parameter types " +
"[mtdName=" + mtd.getName() + ", allowedTypes=" + GridifyUtils.getAllowedMethodParameterTypes() + ']');
}
List<Integer> annParamIdxs = new LinkedList<>();
for (int i = 0; i < paramTypes.length; i++) {
Class<?> paramType = paramTypes[i];
if (GridifyUtils.isMethodParameterTypeAnnotated(paramType.getDeclaredAnnotations()))
annParamIdxs.add(i);
}
if (annParamIdxs.size() > 1) {
throw new IgniteCheckedException("Invalid method signature. Only one method parameter can may annotated with @" +
GridifyInput.class.getSimpleName() +
"[mtdName=" + mtd.getName() + ", allowedTypes=" + GridifyUtils.getAllowedMethodParameterTypes() +
", annParamIdxs=" + annParamIdxs + ']');
}
if (allowedParamIdxs.size() > 1 && annParamIdxs.isEmpty()) {
throw new IgniteCheckedException("Invalid method signature. Method parameter must be annotated with @" +
GridifyInput.class.getSimpleName() +
"[mtdName=" + mtd.getName() + ", allowedTypes=" + GridifyUtils.getAllowedMethodParameterTypes() +
", allowedParamIdxs=" + allowedParamIdxs + ']');
}
if (!annParamIdxs.isEmpty() && !allowedParamIdxs.contains(annParamIdxs.get(0))) {
throw new IgniteCheckedException("Invalid method signature. Invalid annotated parameter " +
"[mtdName=" + mtd.getName() + ", allowedTypes=" + GridifyUtils.getAllowedMethodParameterTypes() +
", allowedParamIdxs=" + allowedParamIdxs + ", annParamIdxs=" + annParamIdxs + ']');
}
if (!GridifyUtils.isMethodReturnTypeValid(mtd.getReturnType())) {
throw new IgniteCheckedException("Invalid method signature. Invalid method return type " +
"[mtdName=" + mtd.getName() + ", allowedTypes=" + GridifyUtils.getAllowedMethodReturnTypes() +
", mtdReturnType=" + mtd.getReturnType() + ']');
}
}
/**
* Check if split allowed for input argument.
* Note, that for argument with unknown elements size with
* default {@link GridifySetToSet#splitSize()} value (default value {@code 0})
* there is no ability to calculate how much jobs should be used in task execution.
*
* @param arg Gridify argument.
* @param ann Annotation
* @throws IgniteCheckedException If split is not allowed with current parameters.
*/
protected void checkIsSplitToJobsAllowed(GridifyRangeArgument arg, GridifySetToSet ann) throws IgniteCheckedException {
if (arg.getInputSize() == UNKNOWN_SIZE && ann.threshold() <= 0 && ann.splitSize() <= 0) {
throw new IgniteCheckedException("Failed to split input method argument to jobs with unknown input size and " +
"invalid annotation parameter 'splitSize' [mtdName=" + arg.getMethodName() + ", inputTypeCls=" +
arg.getMethodParameterTypes()[arg.getParamIndex()].getName() +
", threshold=" + ann.threshold() + ", splitSize=" + ann.splitSize() + ']');
}
}
/**
* Execute method on grid.
*
* @param compute {@link org.apache.ignite.IgniteCompute} instance.
* @param cls Joint point signature class.
* @param arg GridifyArgument with all method signature parameters.
* @param nodeFilter Node filter.
* @param threshold Parameter that defines the minimal value below which the
* execution will NOT be grid-enabled.
* @param splitSize Size of elements to send in job argument.
* @param timeout Execution timeout.
* @return Result.
* @throws IgniteCheckedException If execution failed.
*/
protected Object execute(IgniteCompute compute, Class<?> cls, GridifyRangeArgument arg,
GridifyNodeFilter nodeFilter, int threshold, int splitSize, long timeout) throws IgniteCheckedException {
long now = U.currentTimeMillis();
long end = timeout == 0 ? Long.MAX_VALUE : timeout + now;
// Prevent overflow.
if (end < 0)
end = Long.MAX_VALUE;
if (now > end)
throw new ComputeTaskTimeoutCheckedException("Timeout occurred while waiting for completion.");
Collection<?> res = compute.withTimeout(timeout == 0 ? 0L : (end - now)).execute(
new GridifyDefaultRangeTask(cls, nodeFilter, threshold, splitSize, false),
arg);
return result(arg.getMethodReturnType(), res);
}
/**
* Handle last calculation result.
*
* @param cls Method return type.
* @param taskRes Result of last task execution.
* @return Calculation result.
*/
@SuppressWarnings({"unchecked"})
private Object result(Class<?> cls, Iterable taskRes) {
assert taskRes != null;
Collection<Object> res = new LinkedList<>();
for (Object element : taskRes) {
res.addAll(GridifyUtils.parameterToCollection(element));
}
return GridifyUtils.collectionToParameter(cls, res);
}
}