/*
* Copyright (C) 2012-2013 Facebook, 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 io.nettythrift.core;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import org.apache.thrift.ProcessFunction;
import org.apache.thrift.TBase;
import org.apache.thrift.TBaseProcessor;
import io.netty.channel.ChannelHandler;
import io.netty.channel.socket.SocketChannel;
import io.nettythrift.protocol.ProtocolFactorySelector;
import io.nettythrift.protocol.ProtocolFactorySelectorFactory;
/**
* Descriptor for a Thrift Server. This defines a listener port that Server need
* to start a Thrift endpoint.
*/
public class ThriftServerDef {
// === CONSTANTS ======
// === configurations ===
public final String name;
public final int serverPort;
public final int maxFrameSize;
public final int maxConnections;
public final int queuedResponseLimit;
public final NettyProcessor nettyProcessor;
public final ChannelHandler codecInstaller;
@SuppressWarnings("rawtypes")
public final Map<String, ProcessFunction<?, ? extends TBase>> processMap;
public final Object iface;
public final ExecutorService executor;
public final long clientIdleTimeout;
public final ProtocolFactorySelector protocolFactorySelector;
public final HttpResourceHandler httpResourceHandler;
public final boolean voidMethodDirectReturn;
public final HttpHandlerFactory httpHandlerFactory;
private final int[] voidMethodHashes;
public final TrafficForecast trafficForecast;
public final LogicExecutionStatistics logicExecutionStatistics;
@SuppressWarnings("unchecked")
public ThriftServerDef(String name, int serverPort, int maxFrameSize, int maxConnections, int queuedResponseLimit,
NettyProcessorFactory nettyProcessorFactory, ChannelHandler codecInstaller,
@SuppressWarnings("rawtypes") TBaseProcessor processor, ExecutorService executor, long clientIdleTimeout,
ProtocolFactorySelectorFactory protocolFactorySelectorFactory, HttpResourceHandler httpResourceHandler,
boolean voidMethodDirectReturn, HttpHandlerFactory httpHandlerFactory, TrafficForecastFactory trafficForecastFac,
LogicExecutionStatistics _logicExecutionStatistics) {
super();
this.name = name;
this.serverPort = serverPort;
this.maxFrameSize = maxFrameSize;
this.maxConnections = maxConnections;
this.queuedResponseLimit = queuedResponseLimit;
this.processMap = processor.getProcessMapView();
this.executor = executor;
this.clientIdleTimeout = clientIdleTimeout;
this.httpResourceHandler = httpResourceHandler;
this.voidMethodDirectReturn = voidMethodDirectReturn;
this.httpHandlerFactory = httpHandlerFactory == null ? new DefaultHttpHandlerFactory() : httpHandlerFactory;
if (nettyProcessorFactory == null) {
nettyProcessorFactory = new DefaultNettyProcessorFactory();
}
if (codecInstaller == null) {
codecInstaller = new DefaultChannelInitializer<SocketChannel>(this);
}
this.nettyProcessor = nettyProcessorFactory.create(this);
this.codecInstaller = codecInstaller;
Object iface = null;
try {
Field f = TBaseProcessor.class.getDeclaredField("iface");
f.setAccessible(true);
iface = f.get(processor);
} catch (Exception e) {
e.printStackTrace();
}
Class<?> ifaceClass = null;
{
Class<?> clazz = iface.getClass();
boolean find = false;
while (!find && clazz != null) {
Class<?>[] ifcs = clazz.getInterfaces();
if (ifcs != null && ifcs.length > 0) {
for (Class<?> c : ifcs) {
if (c.getEnclosingClass() != null && c.getSimpleName().equals("Iface")) {
ifaceClass = c;
find = true;
break;
}
}
}
clazz = clazz.getSuperclass();
}
}
Map<String, Integer> inits = Collections.emptyMap();
int[] voidMethodHashes = null;
if (ifaceClass != null) {
inits = new HashMap<>();
Method[] ms = ifaceClass.getMethods();
int len = 0;
int[] hashes = new int[ms.length];
for (Method m : ms) {
if (m.getReturnType() == void.class) {
hashes[len++] = m.getName().hashCode();
inits.put(m.getName(), 128);
} else {
inits.put(m.getName(), 1024);
}
}
if (len > 0) {
if (len < ms.length) {
voidMethodHashes = new int[len];
System.arraycopy(hashes, 0, voidMethodHashes, 0, len);
} else {
voidMethodHashes = hashes;
}
Arrays.sort(voidMethodHashes);
}
}
this.trafficForecast = trafficForecastFac != null ? trafficForecastFac.create(inits)
: new DefaultTrafficForecastImpl(inits, Integer.parseInt(System.getProperty("trafficForecast.logmax", "100")));
this.logicExecutionStatistics = _logicExecutionStatistics != null ? _logicExecutionStatistics
: new DefaultLogicExecutionStatisticsImpl(Integer.parseInt(System.getProperty("ioexe.threshold", "5")),
Integer.parseInt(System.getProperty("ioexe.logmax", "100")));
this.voidMethodHashes = voidMethodHashes;
this.iface = iface;
protocolFactorySelector = protocolFactorySelectorFactory.createProtocolFactorySelector(ifaceClass);
}
public boolean isVoidMethod(String methodName) {
// 目前thrift不支持方法重载,所以可以用方法名唯一确定一个方法
if (voidMethodHashes != null && methodName != null) {
return Arrays.binarySearch(voidMethodHashes, methodName.hashCode()) >= 0;
}
return false;
}
public static ThriftServerDefBuilder newBuilder() {
return new ThriftServerDefBuilder();
}
}