/*
* Copyright 2014 Netflix, Inc.
*
* 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.netflix.ribbon.proxy;
import com.netflix.ribbon.RibbonRequest;
import com.netflix.ribbon.http.HttpRequestBuilder;
import com.netflix.ribbon.http.HttpRequestTemplate.Builder;
import com.netflix.ribbon.http.HttpResourceGroup;
import com.netflix.ribbon.proxy.processor.AnnotationProcessor;
import com.netflix.ribbon.proxy.processor.AnnotationProcessorsProvider;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.reactivex.netty.channel.ContentTransformer;
import io.reactivex.netty.channel.StringTransformer;
import rx.Observable;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* @author Tomasz Bak
*/
class MethodTemplateExecutor {
private static final ContentTransformer<ByteBuf> BYTE_BUF_TRANSFORMER = new ContentTransformer<ByteBuf>() {
@Override
public ByteBuf call(ByteBuf toTransform, ByteBufAllocator byteBufAllocator) {
return toTransform;
}
};
private static final ContentTransformer<byte[]> BYTE_ARRAY_TRANSFORMER = new ContentTransformer<byte[]>() {
@Override
public ByteBuf call(byte[] toTransform, ByteBufAllocator byteBufAllocator) {
ByteBuf byteBuf = byteBufAllocator.buffer(toTransform.length);
byteBuf.writeBytes(toTransform);
return byteBuf;
}
};
private static final StringTransformer STRING_TRANSFORMER = new StringTransformer();
private final HttpResourceGroup httpResourceGroup;
private final MethodTemplate methodTemplate;
private final Builder<?> httpRequestTemplateBuilder;
MethodTemplateExecutor(HttpResourceGroup httpResourceGroup, MethodTemplate methodTemplate, AnnotationProcessorsProvider annotations) {
this.httpResourceGroup = httpResourceGroup;
this.methodTemplate = methodTemplate;
httpRequestTemplateBuilder = createHttpRequestTemplateBuilder();
for (AnnotationProcessor processor: annotations.getProcessors()) {
processor.process(methodTemplate.getTemplateName(), httpRequestTemplateBuilder, methodTemplate.getMethod());
}
}
@SuppressWarnings("unchecked")
public <O> RibbonRequest<O> executeFromTemplate(Object[] args) {
HttpRequestBuilder<?> requestBuilder = httpRequestTemplateBuilder.build().requestBuilder();
withParameters(requestBuilder, args);
withContent(requestBuilder, args);
return (RibbonRequest<O>) requestBuilder.build();
}
private Builder<?> createHttpRequestTemplateBuilder() {
Builder<?> httpRequestTemplateBuilder = createBaseHttpRequestTemplate(httpResourceGroup);
return httpRequestTemplateBuilder;
}
private Builder<?> createBaseHttpRequestTemplate(HttpResourceGroup httpResourceGroup) {
Builder<?> httpRequestTemplate;
if (ByteBuf.class.isAssignableFrom(methodTemplate.getResultType())) {
httpRequestTemplate = httpResourceGroup.newTemplateBuilder(methodTemplate.getTemplateName());
} else {
httpRequestTemplate = httpResourceGroup.newTemplateBuilder(methodTemplate.getTemplateName(), methodTemplate.getResultType());
}
return httpRequestTemplate;
}
private void withParameters(HttpRequestBuilder<?> requestBuilder, Object[] args) {
int length = methodTemplate.getParamSize();
for (int i = 0; i < length; i++) {
String name = methodTemplate.getParamName(i);
Object value = args[methodTemplate.getParamPosition(i)];
requestBuilder.withRequestProperty(name, value);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
private void withContent(HttpRequestBuilder<?> requestBuilder, Object[] args) {
if (methodTemplate.getContentArgPosition() < 0) {
return;
}
Object contentValue = args[methodTemplate.getContentArgPosition()];
if (contentValue instanceof Observable) {
if (ByteBuf.class.isAssignableFrom(methodTemplate.getGenericContentType())) {
requestBuilder.withContent((Observable<ByteBuf>) contentValue);
} else {
ContentTransformer contentTransformer = Utils.newInstance(methodTemplate.getContentTransformerClass());
requestBuilder.withRawContentSource((Observable) contentValue, contentTransformer);
}
} else if (contentValue instanceof ByteBuf) {
requestBuilder.withRawContentSource(Observable.just((ByteBuf) contentValue), BYTE_BUF_TRANSFORMER);
} else if (contentValue instanceof byte[]) {
requestBuilder.withRawContentSource(Observable.just((byte[]) contentValue), BYTE_ARRAY_TRANSFORMER);
} else if (contentValue instanceof String) {
requestBuilder.withRawContentSource(Observable.just((String) contentValue), STRING_TRANSFORMER);
} else {
ContentTransformer contentTransformer = Utils.newInstance(methodTemplate.getContentTransformerClass());
requestBuilder.withRawContentSource(Observable.just(contentValue), contentTransformer);
}
}
public static Map<Method, MethodTemplateExecutor> from(HttpResourceGroup httpResourceGroup, Class<?> clientInterface, AnnotationProcessorsProvider annotations) {
MethodTemplate[] methodTemplates = MethodTemplate.from(clientInterface);
Map<Method, MethodTemplateExecutor> tgm = new HashMap<Method, MethodTemplateExecutor>();
for (MethodTemplate mt : methodTemplates) {
tgm.put(mt.getMethod(), new MethodTemplateExecutor(httpResourceGroup, mt, annotations));
}
return tgm;
}
}