/*
* Copyright (C) 2012-2016 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 com.facebook.nifty.core;
import com.google.common.base.Preconditions;
import org.jboss.netty.util.Timer;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
/*
* Hooks for configuring various parts of Netty.
*/
public abstract class NettyConfigBuilderBase<T extends NettyConfigBuilderBase<T>>
{
// These constants come directly from Netty but are private in Netty.
public static final int DEFAULT_BOSS_THREAD_COUNT = 1;
public static final int DEFAULT_WORKER_THREAD_COUNT = Runtime.getRuntime().availableProcessors() * 2;
private final Map<String, Object> options = new HashMap<>();
private String niftyName;
private int bossThreadCount = DEFAULT_BOSS_THREAD_COUNT;
private int workerThreadCount = DEFAULT_WORKER_THREAD_COUNT;
private ExecutorService bossThreadExecutor;
private ExecutorService workerThreadExecutor;
private Timer timer;
public Map<String, Object> getBootstrapOptions()
{
return Collections.unmodifiableMap(options);
}
/**
* Sets a netty {@link Timer} that will be used to trigger read, write, and connection
* timeouts. Defaults to creating a new {@link org.jboss.netty.util.HashedWheelTimer}
*
* @param timer
* @return
*/
public T setTimer(Timer timer)
{
this.timer = timer;
return (T) this;
}
protected Timer getTimer()
{
return timer;
}
/**
* Sets an identifier which will be added to all thread created by the boss and worker
* executors.
*
* @param niftyName
* @return
*/
protected T setNiftyName(String niftyName)
{
Preconditions.checkNotNull(niftyName, "niftyName cannot be null");
this.niftyName = niftyName;
return (T) this;
}
public String getNiftyName()
{
return niftyName;
}
public T setBossThreadExecutor(ExecutorService bossThreadExecutor)
{
this.bossThreadExecutor = bossThreadExecutor;
return (T) this;
}
protected ExecutorService getBossExecutor()
{
return bossThreadExecutor;
}
/**
* Sets the number of threads that will be used to manage
* @param bossThreadCount
* @return
*/
public T setBossThreadCount(int bossThreadCount)
{
this.bossThreadCount = bossThreadCount;
return (T) this;
}
protected int getBossThreadCount()
{
return bossThreadCount;
}
public T setWorkerThreadExecutor(ExecutorService workerThreadExecutor)
{
this.workerThreadExecutor = workerThreadExecutor;
return (T) this;
}
protected ExecutorService getWorkerExecutor()
{
return workerThreadExecutor;
}
public T setWorkerThreadCount(int workerThreadCount)
{
this.workerThreadCount = workerThreadCount;
return (T) this;
}
protected int getWorkerThreadCount()
{
return workerThreadCount;
}
// Magic alert ! Content of this class is considered ugly and magical.
// For all intents and purposes this is to create a Map with the correct
// key and value pairs for Netty's Bootstrap to consume.
//
// sadly Netty does not define any constant strings whatsoever for the proper key to
// use and it's all based on standard java bean attributes.
//
// A ChannelConfig impl in netty is also tied with a socket, but since all
// these configs are interfaces we can do a bit of magic hacking here.
protected class Magic implements InvocationHandler
{
private final String prefix;
public Magic(String prefix)
{
this.prefix = prefix;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
// we are only interested in setters with single arg
if (proxy != null) {
if (method.getName().equals("toString")) {
return "this is a magic proxy";
}
else if (method.getName().equals("equals")) {
return Boolean.FALSE;
}
else if (method.getName().equals("hashCode")) {
return 0;
}
}
// we don't support multi-arg setters
if (method.getName().startsWith("set") && args.length == 1) {
String attributeName = method.getName().substring(3);
// camelCase it
attributeName = attributeName.substring(0, 1).toLowerCase() + attributeName.substring(1);
// now this is our key
options.put(prefix + attributeName, args[0]);
return null;
}
throw new UnsupportedOperationException();
}
}
}