| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.ftpserver.listener.nio; |
| |
| import java.io.IOException; |
| import java.net.InetAddress; |
| import java.net.InetSocketAddress; |
| import java.security.GeneralSecurityException; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.ftpserver.DataConnectionConfiguration; |
| import org.apache.ftpserver.FtpServerConfigurationException; |
| import org.apache.ftpserver.impl.DefaultFtpHandler; |
| import org.apache.ftpserver.impl.FtpHandler; |
| import org.apache.ftpserver.impl.FtpIoSession; |
| import org.apache.ftpserver.impl.FtpServerContext; |
| import org.apache.ftpserver.ipfilter.MinaSessionFilter; |
| import org.apache.ftpserver.ipfilter.SessionFilter; |
| import org.apache.ftpserver.listener.Listener; |
| import org.apache.ftpserver.listener.ListenerFactory; |
| import org.apache.ftpserver.ssl.ClientAuth; |
| import org.apache.ftpserver.ssl.SslConfiguration; |
| import org.apache.mina.core.session.IdleStatus; |
| import org.apache.mina.core.session.IoSession; |
| import org.apache.mina.filter.codec.ProtocolCodecFilter; |
| import org.apache.mina.filter.executor.ExecutorFilter; |
| import org.apache.mina.filter.firewall.Subnet; |
| import org.apache.mina.filter.logging.MdcInjectionFilter; |
| import org.apache.mina.filter.ssl.SslFilter; |
| import org.apache.mina.transport.socket.SocketAcceptor; |
| import org.apache.mina.transport.socket.nio.NioSocketAcceptor; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * <strong>Internal class, do not use directly.</strong> |
| * |
| * The default {@link Listener} implementation. |
| * |
| * @author <a href="http://mina.apache.org">Apache MINA Project</a> |
| */ |
| public class NioListener extends AbstractListener { |
| |
| private final Logger LOG = LoggerFactory.getLogger(NioListener.class); |
| |
| private SocketAcceptor acceptor; |
| |
| private InetSocketAddress address; |
| |
| boolean suspended = false; |
| |
| private FtpHandler handler = new DefaultFtpHandler(); |
| |
| private FtpServerContext context; |
| |
| /** |
| * @deprecated Use the constructor with IpFilter instead. Constructor for internal use, do not use directly. Instead |
| * use {@link ListenerFactory} |
| */ |
| @Deprecated |
| public NioListener(String serverAddress, int port, boolean implicitSsl, SslConfiguration sslConfiguration, DataConnectionConfiguration dataConnectionConfig, int idleTimeout, List<InetAddress> blockedAddresses, List<Subnet> blockedSubnets) { |
| super(serverAddress, port, implicitSsl, sslConfiguration, dataConnectionConfig, idleTimeout, blockedAddresses, blockedSubnets); |
| } |
| |
| /** |
| * Constructor for internal use, do not use directly. Instead use {@link ListenerFactory} |
| */ |
| public NioListener(String serverAddress, int port, boolean implicitSsl, SslConfiguration sslConfiguration, DataConnectionConfiguration dataConnectionConfig, int idleTimeout, SessionFilter sessionFilter) { |
| super(serverAddress, port, implicitSsl, sslConfiguration, dataConnectionConfig, idleTimeout, sessionFilter); |
| } |
| |
| /** |
| * @see Listener#start(FtpServerContext) |
| */ |
| public synchronized void start(FtpServerContext context) { |
| if (!isStopped()) { |
| // listener already started, don't allow |
| throw new IllegalStateException("Listener already started"); |
| } |
| |
| try { |
| |
| this.context = context; |
| |
| acceptor = new NioSocketAcceptor(Runtime.getRuntime().availableProcessors()); |
| |
| if (getServerAddress() != null) { |
| address = new InetSocketAddress(getServerAddress(), getPort()); |
| } else { |
| address = new InetSocketAddress(getPort()); |
| } |
| |
| acceptor.setReuseAddress(true); |
| acceptor.getSessionConfig().setReadBufferSize(2048); |
| acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, getIdleTimeout()); |
| // Decrease the default receiver buffer size |
| acceptor.getSessionConfig().setReceiveBufferSize(512); |
| |
| MdcInjectionFilter mdcFilter = new MdcInjectionFilter(); |
| |
| acceptor.getFilterChain().addLast("mdcFilter", mdcFilter); |
| |
| SessionFilter sessionFilter = getSessionFilter(); |
| if (sessionFilter != null) { |
| // add and IP filter to the filter chain. |
| acceptor.getFilterChain().addLast("sessionFilter", new MinaSessionFilter(sessionFilter)); |
| } |
| |
| acceptor.getFilterChain().addLast("threadPool", new ExecutorFilter(context.getThreadPoolExecutor())); |
| acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new FtpServerProtocolCodecFactory())); |
| acceptor.getFilterChain().addLast("mdcFilter2", mdcFilter); |
| acceptor.getFilterChain().addLast("logger", new FtpLoggingFilter()); |
| |
| if (isImplicitSsl()) { |
| SslConfiguration ssl_conf = getSslConfiguration(); |
| SslFilter ssl_filter; |
| try { |
| ssl_filter = new SslFilter(ssl_conf.getSSLContext()); |
| } catch (GeneralSecurityException e) { |
| throw new FtpServerConfigurationException("SSL could not be initialized, check configuration"); |
| } |
| |
| if (ssl_conf.getClientAuth() == ClientAuth.NEED) { |
| ssl_filter.setNeedClientAuth(true); |
| } else if (ssl_conf.getClientAuth() == ClientAuth.WANT) { |
| ssl_filter.setWantClientAuth(true); |
| } |
| |
| if (ssl_conf.getEnabledProtocol() != null) { |
| ssl_filter.setEnabledProtocols(new String[] { ssl_conf.getEnabledProtocol() }); |
| } |
| |
| if (ssl_conf.getEnabledCipherSuites() != null) { |
| ssl_filter.setEnabledCipherSuites(ssl_conf.getEnabledCipherSuites()); |
| } |
| |
| acceptor.getFilterChain().addFirst("sslFilter", ssl_filter); |
| } |
| |
| handler.init(context, this); |
| acceptor.setHandler(new FtpHandlerAdapter(context, handler)); |
| |
| try { |
| acceptor.bind(address); |
| } catch (IOException e) { |
| throw new FtpServerConfigurationException("Failed to bind to address " + address + ", check configuration", e); |
| } |
| |
| updatePort(); |
| |
| } catch (RuntimeException e) { |
| // clean up if we fail to start |
| stop(); |
| |
| throw e; |
| } |
| } |
| |
| private void updatePort() { |
| // update the port to the real port bound by the listener |
| setPort(acceptor.getLocalAddress().getPort()); |
| } |
| |
| /** |
| * @see Listener#stop() |
| */ |
| public synchronized void stop() { |
| // close server socket |
| if (acceptor != null) { |
| acceptor.unbind(); |
| acceptor.dispose(); |
| acceptor = null; |
| } |
| context = null; |
| } |
| |
| /** |
| * @see Listener#isStopped() |
| */ |
| public boolean isStopped() { |
| return acceptor == null; |
| } |
| |
| /** |
| * @see Listener#isSuspended() |
| */ |
| public boolean isSuspended() { |
| return suspended; |
| |
| } |
| |
| /** |
| * @see Listener#resume() |
| */ |
| public synchronized void resume() { |
| if (acceptor != null && suspended) { |
| try { |
| LOG.debug("Resuming listener"); |
| acceptor.bind(address); |
| LOG.debug("Listener resumed"); |
| |
| updatePort(); |
| |
| suspended = false; |
| } catch (IOException e) { |
| LOG.error("Failed to resume listener", e); |
| } |
| } |
| } |
| |
| /** |
| * @see Listener#suspend() |
| */ |
| public synchronized void suspend() { |
| if (acceptor != null && !suspended) { |
| LOG.debug("Suspending listener"); |
| acceptor.unbind(); |
| |
| suspended = true; |
| LOG.debug("Listener suspended"); |
| } |
| } |
| |
| /** |
| * @see Listener#getActiveSessions() |
| */ |
| public synchronized Set<FtpIoSession> getActiveSessions() { |
| Map<Long, IoSession> sessions = acceptor.getManagedSessions(); |
| |
| Set<FtpIoSession> ftpSessions = new HashSet<FtpIoSession>(); |
| for (IoSession session : sessions.values()) { |
| ftpSessions.add(new FtpIoSession(session, context)); |
| } |
| return ftpSessions; |
| } |
| } |