/*
* Copyright 2014 Avanza Bank AB
*
* 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.avanza.astrix.gs.remoting;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.openspaces.remoting.Routing;
import com.avanza.astrix.core.AstrixBroadcast;
import com.avanza.astrix.core.remoting.Router;
import com.avanza.astrix.core.remoting.RoutingStrategy;
import com.avanza.astrix.remoting.client.AmbiguousRoutingException;
import com.avanza.astrix.remoting.client.AnnotatedArgumentInstanceRouter;
import com.avanza.astrix.remoting.client.AnnotatedArgumentRouter;
import com.avanza.astrix.remoting.client.BroadcastRouter;
import com.avanza.astrix.remoting.client.DefaultAstrixRoutingStrategy;
import com.avanza.astrix.remoting.client.PropertyOnAnnotatedArgumentRoutingStrategy;
import com.avanza.astrix.remoting.util.RoutingKeyMethodCache;
import com.gigaspaces.annotation.pojo.SpaceRouting;
/**
* Provides a routing strategy compatible with the routing used by native
* gigaspaces remoting, (@Routing annotated arguments).
*
* @author Elias Lindholm
*
*/
public class GsRoutingStrategy implements RoutingStrategy {
private static final RoutingKeyMethodCache<SpaceRouting> routingKeyMethodCache = new RoutingKeyMethodCache<>(SpaceRouting.class);
@Override
public Router create(Method serviceMethod) {
return createRoutingStrategy(serviceMethod);
}
private Router createRoutingStrategy(Method m) {
Router result = new DefaultAstrixRoutingStrategy().create(m);
if (result != null) {
return result;
}
result = lookForRoutingAnnotationInMethodSignature(m);
if (result != null) {
return result;
}
result = lookForRoutingAnnotationOnMethodArguments(m);
if (result != null) {
return result;
}
if (m.getAnnotation(AstrixBroadcast.class) == null) {
throw new AmbiguousRoutingException(String.format("Ambiguous routing. No routing argument defined in method signature or on method " +
"arguments and method not annotated with @AstrixBroadcast. serviceMethod=%s", m.toString()));
}
return new BroadcastRouter();
}
private Router lookForRoutingAnnotationOnMethodArguments(Method m) {
Router result = null;
for (int argumentIndex = 0; argumentIndex < m.getParameterTypes().length; argumentIndex++) {
Method routingKeyMethod = routingKeyMethodCache.getRoutingKeyMethod(m.getParameterTypes()[argumentIndex]);
if (routingKeyMethod != null) {
if (result != null) {
throw new AmbiguousRoutingException(String.format("Ambiguous routing, multiple arguments with @SpaceRouting annotated methods." +
" Use @Routing on one service argument to identify routing method, or @AstrixBroadcast for broadcast" +
" operations. service method=%s", m.toString()));
}
result = new AnnotatedArgumentInstanceRouter(argumentIndex, routingKeyMethod);
}
}
return result;
}
public static Router lookForRoutingAnnotationInMethodSignature(Method m) {
Router routingStrategy = null;
for (int argumentIndex = 0; argumentIndex < m.getParameterTypes().length; argumentIndex++) {
for (Annotation a : m.getParameterAnnotations()[argumentIndex]) {
if (a.annotationType().equals(Routing.class)) {
if (routingStrategy != null) {
throw new AmbiguousRoutingException(String.format("Ambiguous routing, multiple @Routing annotated methods on %s", m.toString()));
}
routingStrategy = createRoutingStrategy(m, argumentIndex, (Routing) a);
}
}
}
return routingStrategy;
}
public static Router createRoutingStrategy(Method serviceMethod, int routingArgumentIndex, Routing routingAnnotation) {
if (routingAnnotation.value().trim().isEmpty()) {
return new AnnotatedArgumentRouter(routingArgumentIndex);
}
String targetRoutingMethod = routingAnnotation.value();
try {
Class<?> type = serviceMethod.getParameterTypes()[routingArgumentIndex];
Method method = type.getMethod(targetRoutingMethod);
return new PropertyOnAnnotatedArgumentRoutingStrategy(routingArgumentIndex, method);
} catch (NoSuchMethodException | SecurityException e) {
throw new IllegalArgumentException("Cant route using: " + targetRoutingMethod , e);
}
}
}