/*
* RED5 Open Source Flash Server - http://code.google.com/p/red5/
*
* Copyright 2006-2012 by respective authors (see below). All rights reserved.
*
* 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 org.red5.server.net.rtmp;
import java.lang.management.ManagementFactory;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.StandardMBean;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.buffer.SimpleBufferAllocator;
import org.apache.mina.core.service.AbstractIoService;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.service.IoServiceStatistics;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.red5.server.jmx.mxbeans.RTMPMinaTransportMXBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Transport setup class configures socket acceptor and thread pools for RTMP in Mina.
*
* <br />
* <i>Note: This code originates from AsyncWeb. Originally modified by Luke Hubbard.</i>
* <br />
*
* @author Luke Hubbard
* @author Paul Gregoire
*/
public class RTMPMinaTransport implements RTMPMinaTransportMXBean {
private static final Logger log = LoggerFactory.getLogger(RTMPMinaTransport.class);
protected SocketAcceptor acceptor;
protected Set<SocketAddress> addresses = new HashSet<SocketAddress>();
protected IoHandlerAdapter ioHandler;
protected IoServiceStatistics stats;
protected int ioThreads = Runtime.getRuntime().availableProcessors() * 2;
/**
* MBean object name used for de/registration purposes.
*/
protected ObjectName serviceManagerObjectName;
protected boolean enableMinaMonitor = false;
protected int minaPollInterval = 1000;
protected boolean tcpNoDelay = true;
protected boolean useHeapBuffers = true;
protected int sendBufferSize = 65536;
protected int receiveBufferSize = 65536;
protected int trafficClass = 0x08 | 0x10;
private void initIOHandler() {
if (ioHandler == null) {
log.info("No RTMP IO Handler associated - using defaults");
ioHandler = new RTMPMinaIoHandler();
}
}
public void start() throws Exception {
initIOHandler();
IoBuffer.setUseDirectBuffer(!useHeapBuffers); // this is global, oh well
if (useHeapBuffers) {
// dont pool for heap buffers
IoBuffer.setAllocator(new SimpleBufferAllocator());
}
log.info("RTMP Mina Transport Settings");
log.info("I/O Threads: {}", ioThreads);
// XXX Paul: come back and review why the separate executors didnt work as expected
// ref: http://stackoverflow.com/questions/5088850/multi-threading-in-red5
//use default parameters, and given number of NioProcessor for multithreading I/O operations
acceptor = new NioSocketAcceptor(ioThreads);
// set acceptor props
acceptor.setHandler(ioHandler);
acceptor.setBacklog(12);
//get the current session config that would be used during create
SocketSessionConfig sessionConf = acceptor.getSessionConfig();
//reuse the addresses
sessionConf.setReuseAddress(true);
log.info("TCP No Delay: {}", tcpNoDelay);
sessionConf.setTcpNoDelay(tcpNoDelay);
sessionConf.setSendBufferSize(sendBufferSize);
sessionConf.setReceiveBufferSize(receiveBufferSize);
// set the traffic class - http://docs.oracle.com/javase/6/docs/api/java/net/Socket.html#setTrafficClass(int)
// IPTOS_LOWCOST (0x02)
// IPTOS_RELIABILITY (0x04)
// IPTOS_THROUGHPUT (0x08) *
// IPTOS_LOWDELAY (0x10) *
sessionConf.setTrafficClass(trafficClass);
// get info
log.info("Settings - send buffer size: {} recv buffer size: {} so linger: {} traffic class: {}",
new Object[] { sessionConf.getSendBufferSize(), sessionConf.getReceiveBufferSize(), sessionConf.getSoLinger(), sessionConf.getTrafficClass() });
//set reuse address on the socket acceptor as well
acceptor.setReuseAddress(true);
String addrStr = addresses.toString();
log.debug("Binding to {}", addrStr);
acceptor.bind(addresses);
//create a new mbean for this instance
// RTMPMinaTransport
String cName = this.getClass().getName();
if (cName.indexOf('.') != -1) {
cName = cName.substring(cName.lastIndexOf('.')).replaceFirst("[\\.]", "");
}
//enable only if user wants it
if (enableMinaMonitor) {
//add a service manager to allow for more introspection into the workings of mina
stats = new IoServiceStatistics((AbstractIoService) acceptor);
//poll every second
stats.setThroughputCalculationInterval(minaPollInterval);
//construct a object containing all the host and port combos
StringBuilder addressAndPorts = new StringBuilder();
for (SocketAddress sa : addresses) {
InetSocketAddress isa = ((InetSocketAddress) sa);
if (!isa.isUnresolved()) {
addressAndPorts.append(isa.getHostName());
addressAndPorts.append('|');
addressAndPorts.append(isa.getPort());
addressAndPorts.append(';');
}
}
addressAndPorts.deleteCharAt(addressAndPorts.length() - 1);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
try {
serviceManagerObjectName = new ObjectName("org.red5.server:type=IoServiceManager,addresses=" + addressAndPorts.toString());
mbs.registerMBean(new StandardMBean(this, RTMPMinaTransportMXBean.class, true), serviceManagerObjectName);
} catch (Exception e) {
log.warn("Error on jmx registration", e);
}
}
}
public void stop() {
log.info("RTMP Mina Transport unbind");
acceptor.unbind();
// deregister with jmx
if (serviceManagerObjectName != null) {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
try {
mbs.unregisterMBean(serviceManagerObjectName);
} catch (Exception e) {
log.warn("Error on jmx unregistration", e);
}
}
}
public void setConnector(InetSocketAddress connector) {
addresses.add(connector);
log.info("RTMP Mina Transport bound to {}", connector.toString());
}
public void setConnectors(List<InetSocketAddress> connectors) {
for (InetSocketAddress addr : connectors) {
addresses.add(addr);
log.info("RTMP Mina Transport bound to {}", addr.toString());
}
}
public void setIoHandler(IoHandlerAdapter rtmpIOHandler) {
this.ioHandler = rtmpIOHandler;
}
public void setIoThreads(int ioThreads) {
this.ioThreads = ioThreads;
}
/**
* @param sendBufferSize the sendBufferSize to set
*/
public void setSendBufferSize(int sendBufferSize) {
this.sendBufferSize = sendBufferSize;
}
/**
* @param receiveBufferSize the receiveBufferSize to set
*/
public void setReceiveBufferSize(int receiveBufferSize) {
this.receiveBufferSize = receiveBufferSize;
}
/**
* @param trafficClass the trafficClass to set
*/
public void setTrafficClass(int trafficClass) {
this.trafficClass = trafficClass;
}
public void setTcpNoDelay(boolean tcpNoDelay) {
this.tcpNoDelay = tcpNoDelay;
}
public void setUseHeapBuffers(boolean useHeapBuffers) {
this.useHeapBuffers = useHeapBuffers;
}
/**
* @param enableMinaMonitor the enableMinaMonitor to set
*/
public void setEnableMinaMonitor(boolean enableMinaMonitor) {
this.enableMinaMonitor = enableMinaMonitor;
}
public void setMinaPollInterval(int minaPollInterval) {
this.minaPollInterval = minaPollInterval;
}
public String toString() {
return String.format("RTMP Mina Transport %s", addresses.toString());
}
}