/*
* Copyright 2011-2017 the original author or authors.
*
* 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.lambdaworks.redis;
import java.net.SocketAddress;
import java.util.concurrent.ThreadFactory;
import com.lambdaworks.redis.internal.LettuceAssert;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollDomainSocketChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
/**
* Wraps and provides Epoll classes. This is to protect the user from {@link ClassNotFoundException}'s caused by the absence of
* the {@literal netty-transport-native-epoll} library during runtime. Internal API.
*
* @author Mark Paluch
* @since 4.4
*/
public class EpollProvider {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(EpollProvider.class);
private static final String EPOLL_ENABLED_KEY = "io.lettuce.core.epoll";
private static final boolean EPOLL_ENABLED = Boolean.parseBoolean(SystemPropertyUtil.get(EPOLL_ENABLED_KEY, "true"));
private static final boolean EPOLL_AVAILABLE;
private static final EpollResources epollResources;
static {
boolean availability = false;
try {
Class.forName("io.netty.channel.epoll.Epoll");
availability = Epoll.isAvailable();
} catch (ClassNotFoundException e) {
}
EPOLL_AVAILABLE = availability;
if (EPOLL_AVAILABLE) {
logger.warn("Starting with Epoll library");
epollResources = AvailableEpollResources.INSTANCE;
} else {
logger.warn("Starting without optional Epoll library");
epollResources = UnavailableEpollResources.INSTANCE;
}
}
/**
* @return {@literal true} if epoll is available.
*/
public static boolean isAvailable() {
return EPOLL_AVAILABLE && EPOLL_ENABLED;
}
/**
* Check whether the Epoll library is available on the class path.
*
* @throws IllegalStateException if the {@literal netty-transport-native-epoll} library is not available
*/
static void checkForEpollLibrary() {
LettuceAssert.assertState(EPOLL_ENABLED,
String.format("epoll use is disabled via System properties (%s)", EPOLL_ENABLED_KEY));
LettuceAssert
.assertState(
isAvailable(),
"netty-transport-native-epoll is not available. Make sure netty-transport-native-epoll library on the class path and supported by your operating system.");
}
/**
* @param type must not be {@literal null}.
* @return {@literal true} if {@code type} is a {@link io.netty.channel.epoll.EpollEventLoopGroup}.
*/
public static boolean isEventLoopGroup(Class<? extends EventExecutorGroup> type) {
return epollResources.isEventLoopGroup(type);
}
/**
* Create a new {@link io.netty.channel.epoll.EpollEventLoopGroup}.
*
* @param nThreads
* @param threadFactory
* @return the {@link EventLoopGroup}.
*/
public static EventLoopGroup newEventLoopGroup(int nThreads, ThreadFactory threadFactory) {
return epollResources.newEventLoopGroup(nThreads, threadFactory);
}
/**
* @return the {@link io.netty.channel.epoll.EpollDomainSocketChannel} class.
*/
static Class<? extends Channel> domainSocketChannelClass() {
return epollResources.domainSocketChannelClass();
}
/**
* @return the {@link io.netty.channel.epoll.EpollSocketChannel} class.
*/
static Class<? extends Channel> socketChannelClass() {
return epollResources.socketChannelClass();
}
/**
* @return the {@link io.netty.channel.epoll.EpollEventLoopGroup} class.
*/
static Class<? extends EventLoopGroup> eventLoopGroupClass() {
return epollResources.eventLoopGroupClass();
}
static SocketAddress newSocketAddress(String socketPath) {
return epollResources.newSocketAddress(socketPath);
}
/**
* @author Mark Paluch
*/
public interface EpollResources {
/**
* @param type must not be {@literal null}.
* @return {@literal true} if {@code type} is a {@link EpollEventLoopGroup}.
*/
boolean isEventLoopGroup(Class<? extends EventExecutorGroup> type);
/**
* Create a new {@link EpollEventLoopGroup}.
*
* @param nThreads
* @param threadFactory
* @return the {@link EventLoopGroup}.
*/
EventLoopGroup newEventLoopGroup(int nThreads, ThreadFactory threadFactory);
/**
* @return the {@link EpollDomainSocketChannel} class.
*/
Class<? extends Channel> domainSocketChannelClass();
/**
* @return the {@link EpollSocketChannel} class.
*/
Class<? extends Channel> socketChannelClass();
/**
* @return the {@link EpollEventLoopGroup} class.
*/
Class<? extends EventLoopGroup> eventLoopGroupClass();
SocketAddress newSocketAddress(String socketPath);
}
/**
* {@link EpollResources} for unavailable EPoll.
*/
enum UnavailableEpollResources implements EpollResources {
INSTANCE;
@Override
public Class<? extends Channel> domainSocketChannelClass() {
checkForEpollLibrary();
return null;
}
@Override
public Class<? extends EventLoopGroup> eventLoopGroupClass() {
checkForEpollLibrary();
return null;
}
@Override
public boolean isEventLoopGroup(Class<? extends EventExecutorGroup> type) {
checkForEpollLibrary();
return false;
}
@Override
public EventLoopGroup newEventLoopGroup(int nThreads, ThreadFactory threadFactory) {
checkForEpollLibrary();
return null;
}
@Override
public SocketAddress newSocketAddress(String socketPath) {
checkForEpollLibrary();
return null;
}
@Override
public Class<? extends Channel> socketChannelClass() {
checkForEpollLibrary();
return null;
}
}
/**
* {@link EpollResources} for available Epoll.
*/
enum AvailableEpollResources implements EpollResources {
INSTANCE;
@Override
public boolean isEventLoopGroup(Class<? extends EventExecutorGroup> type) {
LettuceAssert.notNull(type, "EventLoopGroup type must not be null");
return type.equals(EpollEventLoopGroup.class);
}
@Override
public EventLoopGroup newEventLoopGroup(int nThreads, ThreadFactory threadFactory) {
checkForEpollLibrary();
return new EpollEventLoopGroup(nThreads, threadFactory);
}
@Override
public Class<? extends Channel> domainSocketChannelClass() {
checkForEpollLibrary();
return EpollDomainSocketChannel.class;
}
@Override
public Class<? extends Channel> socketChannelClass() {
checkForEpollLibrary();
return EpollSocketChannel.class;
}
@Override
public Class<? extends EventLoopGroup> eventLoopGroupClass() {
checkForEpollLibrary();
return EpollEventLoopGroup.class;
}
@Override
public SocketAddress newSocketAddress(String socketPath) {
checkForEpollLibrary();
return new DomainSocketAddress(socketPath);
}
}
}