Example usage for io.netty.util.concurrent GenericFutureListener GenericFutureListener

List of usage examples for io.netty.util.concurrent GenericFutureListener GenericFutureListener

Introduction

In this page you can find the example usage for io.netty.util.concurrent GenericFutureListener GenericFutureListener.

Prototype

GenericFutureListener

Source Link

Usage

From source file:com.relayrides.pushy.apns.ApnsClientThread.java

License:Open Source License

private void sendNextNotification(final long timeout, final TimeUnit timeUnit) throws InterruptedException {
    final T notification = this.pushManager.getQueue().poll(timeout, timeUnit);

    if (this.isInterrupted()) {
        this.pushManager.enqueuePushNotification(notification);
    } else if (notification != null) {
        final SendableApnsPushNotification<T> sendableNotification = new SendableApnsPushNotification<T>(
                notification, this.sequenceNumber++);

        final String threadName = this.getName();

        if (log.isTraceEnabled()) {
            log.trace(String.format("%s sending %s", threadName, sendableNotification));
        }/*from  w  w  w. java 2s . co  m*/

        this.sentNotificationBuffer.addSentNotification(sendableNotification);

        this.channel.write(sendableNotification).addListener(new GenericFutureListener<ChannelFuture>() {

            public void operationComplete(final ChannelFuture future) {
                if (future.cause() != null) {
                    if (log.isTraceEnabled()) {
                        log.trace(String.format("%s failed to write notification %s", threadName,
                                sendableNotification), future.cause());
                    }

                    requestReconnection();

                    // Delivery failed for some IO-related reason; re-enqueue for another attempt, but
                    // only if the notification is in the sent notification buffer (i.e. if it hasn't
                    // been re-enqueued for another reason).
                    final T failedNotification = sentNotificationBuffer
                            .getAndRemoveNotificationWithSequenceNumber(
                                    sendableNotification.getSequenceNumber());

                    if (failedNotification != null) {
                        pushManager.enqueuePushNotification(failedNotification);
                    }
                } else {
                    if (log.isTraceEnabled()) {
                        log.trace(String.format("%s successfully wrote notification %d", threadName,
                                sendableNotification.getSequenceNumber()));
                    }
                }
            }
        });

        this.hasEverSentNotification = true;

        if (++this.writesSinceLastFlush >= ApnsClientThread.BATCH_SIZE) {
            this.channel.flush();
            this.writesSinceLastFlush = 0;
        }
    } else {
        if (this.writesSinceLastFlush > 0) {
            this.channel.flush();
            this.writesSinceLastFlush = 0;
        }
    }
}

From source file:com.relayrides.pushy.apns.ApnsConnection.java

License:Open Source License

/**
 * Asynchronously connects to the APNs gateway in this connection's environment. The outcome of the connection
 * attempt is reported via this connection's listener.
 *
 * @see ApnsConnectionListener#handleConnectionSuccess(ApnsConnection)
 * @see ApnsConnectionListener#handleConnectionFailure(ApnsConnection, Throwable)
 *///from w ww  . j ava2s.c  o  m
@SuppressWarnings("deprecation")
public synchronized void connect() {

    final ApnsConnection<T> apnsConnection = this;

    if (this.connectFuture != null) {
        throw new IllegalStateException(String.format("%s already started a connection attempt.", this.name));
    }

    final Bootstrap bootstrap = new Bootstrap();
    bootstrap.group(this.eventLoopGroup);
    bootstrap.channel(NioSocketChannel.class);
    bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
    bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);

    // TODO Remove this when Netty 5 is available
    bootstrap.option(ChannelOption.AUTO_CLOSE, false);

    bootstrap.handler(new ChannelInitializer<SocketChannel>() {

        @Override
        protected void initChannel(final SocketChannel channel) {
            final ChannelPipeline pipeline = channel.pipeline();

            final SSLEngine sslEngine = apnsConnection.sslContext.createSSLEngine();
            sslEngine.setUseClientMode(true);

            pipeline.addLast("ssl", new SslHandler(sslEngine));
            pipeline.addLast("decoder", new RejectedNotificationDecoder());
            pipeline.addLast("encoder", new ApnsPushNotificationEncoder());
            pipeline.addLast("handler", new ApnsConnectionHandler(apnsConnection));
        }
    });

    log.debug("{} beginning connection process.", apnsConnection.name);
    this.connectFuture = bootstrap.connect(this.environment.getApnsGatewayHost(),
            this.environment.getApnsGatewayPort());
    this.connectFuture.addListener(new GenericFutureListener<ChannelFuture>() {

        public void operationComplete(final ChannelFuture connectFuture) {
            if (connectFuture.isSuccess()) {
                log.debug("{} connected; waiting for TLS handshake.", apnsConnection.name);

                final SslHandler sslHandler = connectFuture.channel().pipeline().get(SslHandler.class);

                try {
                    sslHandler.handshakeFuture().addListener(new GenericFutureListener<Future<Channel>>() {

                        public void operationComplete(final Future<Channel> handshakeFuture) {
                            if (handshakeFuture.isSuccess()) {
                                log.debug("{} successfully completed TLS handshake.", apnsConnection.name);

                                apnsConnection.handshakeCompleted = true;
                                apnsConnection.listener.handleConnectionSuccess(apnsConnection);
                            } else {
                                log.debug("{} failed to complete TLS handshake with APNs gateway.",
                                        apnsConnection.name, handshakeFuture.cause());

                                connectFuture.channel().close();
                                apnsConnection.listener.handleConnectionFailure(apnsConnection,
                                        handshakeFuture.cause());
                            }
                        }
                    });
                } catch (NullPointerException e) {
                    log.warn("{} failed to get SSL handler and could not wait for a TLS handshake.",
                            apnsConnection.name);

                    connectFuture.channel().close();
                    apnsConnection.listener.handleConnectionFailure(apnsConnection, e);
                }
            } else {
                log.debug("{} failed to connect to APNs gateway.", apnsConnection.name, connectFuture.cause());

                apnsConnection.listener.handleConnectionFailure(apnsConnection, connectFuture.cause());
            }
        }
    });
}

From source file:com.relayrides.pushy.apns.ApnsConnection.java

License:Open Source License

/**
 * Asynchronously sends a push notification to the connected APNs gateway. Successful notifications are
 * <strong>not</strong> acknowledged by the APNs gateway; failed attempts to write push notifications to the
 * outbound buffer and notification rejections are reported via this connection's listener.
 *
 * @param notification the notification to send
 *
 * @see ApnsConnectionListener#handleWriteFailure(ApnsConnection, ApnsPushNotification, Throwable)
 * @see ApnsConnectionListener#handleRejectedNotification(ApnsConnection, ApnsPushNotification, RejectedNotificationReason)
 *///from  w w w .j a v a2  s.co m
public synchronized void sendNotification(final T notification) {
    final ApnsConnection<T> apnsConnection = this;

    if (!this.handshakeCompleted) {
        throw new IllegalStateException(String.format("%s has not completed handshake.", this.name));
    }

    this.connectFuture.channel().eventLoop().execute(new Runnable() {

        public void run() {
            final SendableApnsPushNotification<T> sendableNotification = new SendableApnsPushNotification<T>(
                    notification, apnsConnection.sequenceNumber++);

            log.trace("{} sending {}", apnsConnection.name, sendableNotification);

            apnsConnection.pendingWriteCount += 1;

            apnsConnection.connectFuture.channel().writeAndFlush(sendableNotification)
                    .addListener(new GenericFutureListener<ChannelFuture>() {

                        public void operationComplete(final ChannelFuture writeFuture) {
                            if (writeFuture.isSuccess()) {
                                log.trace("{} successfully wrote notification {}", apnsConnection.name,
                                        sendableNotification.getSequenceNumber());

                                if (apnsConnection.rejectionReceived) {
                                    // Even though the write succeeded, we know for sure that this notification was never
                                    // processed by the gateway because it had already rejected another notification from
                                    // this connection.
                                    apnsConnection.listener.handleUnprocessedNotifications(apnsConnection,
                                            java.util.Collections.singletonList(notification));
                                } else {
                                    apnsConnection.sentNotificationBuffer
                                            .addSentNotification(sendableNotification);
                                }
                            } else {
                                log.trace("{} failed to write notification {}", apnsConnection.name,
                                        sendableNotification, writeFuture.cause());

                                // Assume this is a temporary failure (we know it's not a permanent rejection because we didn't
                                // even manage to write the notification to the wire) and re-enqueue for another send attempt.
                                apnsConnection.listener.handleWriteFailure(apnsConnection, notification,
                                        writeFuture.cause());
                            }

                            apnsConnection.pendingWriteCount -= 1;
                            assert apnsConnection.pendingWriteCount >= 0;

                            if (apnsConnection.pendingWriteCount == 0) {
                                synchronized (apnsConnection.pendingWriteMonitor) {
                                    apnsConnection.pendingWriteMonitor.notifyAll();
                                }
                            }
                        }
                    });
        }
    });
}

From source file:com.relayrides.pushy.apns.ApnsConnection.java

License:Open Source License

/**
 * <p>Gracefully and asynchronously shuts down this connection. Graceful disconnection is triggered by sending a
 * known-bad notification to the APNs gateway; when the gateway rejects the notification, it is guaranteed that
 * preceding notifications were processed successfully and that all following notifications were not processed at
 * all. The gateway will close the connection after rejecting the notification, and this connection's listener will
 * be notified when the connection is closed.</p>
 *
 * <p>Note that if/when the known-bad notification is rejected by the APNs gateway, this connection's listener will
 * <em>not</em> be notified of the rejection.</p>
 *
 * <p>Calling this method before establishing a connection with the APNs gateway or while a graceful shutdown
 * attempt is already in progress has no effect.</p>
 *
 * @see ApnsConnectionListener#handleRejectedNotification(ApnsConnection, ApnsPushNotification, RejectedNotificationReason)
 * @see ApnsConnectionListener#handleConnectionClosure(ApnsConnection)
 *//* w  ww  .j a  v  a2 s .c om*/
public synchronized void shutdownGracefully() {

    final ApnsConnection<T> apnsConnection = this;

    // We only need to send a known-bad notification if we were ever connected in the first place and if we're
    // still connected.
    if (this.handshakeCompleted && this.connectFuture.channel().isActive()) {

        this.connectFuture.channel().eventLoop().execute(new Runnable() {

            public void run() {
                // Don't send a second shutdown notification if we've already started the graceful shutdown process.
                if (apnsConnection.shutdownNotification == null) {

                    log.debug("{} sending known-bad notification to shut down.", apnsConnection.name);

                    apnsConnection.shutdownNotification = new SendableApnsPushNotification<KnownBadPushNotification>(
                            new KnownBadPushNotification(), apnsConnection.sequenceNumber++);

                    apnsConnection.pendingWriteCount += 1;

                    apnsConnection.connectFuture.channel().writeAndFlush(apnsConnection.shutdownNotification)
                            .addListener(new GenericFutureListener<ChannelFuture>() {

                                public void operationComplete(final ChannelFuture future) {
                                    if (future.isSuccess()) {
                                        log.trace("{} successfully wrote known-bad notification {}",
                                                apnsConnection.name,
                                                apnsConnection.shutdownNotification.getSequenceNumber());
                                    } else {
                                        log.trace("{} failed to write known-bad notification {}",
                                                apnsConnection.name, apnsConnection.shutdownNotification,
                                                future.cause());

                                        // Try again!
                                        apnsConnection.shutdownNotification = null;
                                        apnsConnection.shutdownGracefully();
                                    }

                                    apnsConnection.pendingWriteCount -= 1;
                                    assert apnsConnection.pendingWriteCount >= 0;

                                    if (apnsConnection.pendingWriteCount == 0) {
                                        synchronized (apnsConnection.pendingWriteMonitor) {
                                            apnsConnection.pendingWriteMonitor.notifyAll();
                                        }
                                    }
                                }
                            });
                }
            }
        });
    } else {
        // While we can't guarantee that the handshake won't complete in another thread, we CAN guarantee that no
        // new notifications will be sent until shutdownImmediately happens because everything is synchronized.
        this.shutdownImmediately();
    }
}

From source file:com.relayrides.pushy.apns.FeedbackServiceConnection.java

License:Open Source License

/**
 * <p>Connects to the APNs feedback service and waits for expired tokens to arrive. Be warned that this is a
 * <strong>destructive operation</strong>. According to Apple's documentation:</p>
 *
 * <blockquote>The feedback service's list is cleared after you read it. Each time you connect to the feedback
 * service, the information it returns lists only the failures that have happened since you last
 * connected.</blockquote>/*  w w w . j a  va2 s  . c o m*/
 */
public synchronized void connect() {

    if (this.connectFuture != null) {
        throw new IllegalStateException(String.format("%s already started a connection attempt.", this.name));
    }

    final Bootstrap bootstrap = new Bootstrap();
    bootstrap.group(this.eventLoopGroup);
    bootstrap.channel(NioSocketChannel.class);

    final FeedbackServiceConnection feedbackConnection = this;
    bootstrap.handler(new ChannelInitializer<SocketChannel>() {

        @Override
        protected void initChannel(final SocketChannel channel) throws Exception {
            final ChannelPipeline pipeline = channel.pipeline();

            final SSLEngine sslEngine = feedbackConnection.sslContext.createSSLEngine();
            sslEngine.setUseClientMode(true);

            pipeline.addLast("ssl", new SslHandler(sslEngine));
            pipeline.addLast("readTimeoutHandler",
                    new ReadTimeoutHandler(feedbackConnection.configuration.getReadTimeout()));
            pipeline.addLast("decoder", new ExpiredTokenDecoder());
            pipeline.addLast("handler", new FeedbackClientHandler(feedbackConnection));
        }
    });

    this.connectFuture = bootstrap.connect(this.environment.getFeedbackHost(),
            this.environment.getFeedbackPort());
    this.connectFuture.addListener(new GenericFutureListener<ChannelFuture>() {

        @Override
        public void operationComplete(final ChannelFuture connectFuture) {

            if (connectFuture.isSuccess()) {
                log.debug("{} connected; waiting for TLS handshake.", feedbackConnection.name);

                final SslHandler sslHandler = connectFuture.channel().pipeline().get(SslHandler.class);

                try {
                    sslHandler.handshakeFuture().addListener(new GenericFutureListener<Future<Channel>>() {

                        @Override
                        public void operationComplete(final Future<Channel> handshakeFuture) {
                            if (handshakeFuture.isSuccess()) {
                                log.debug("{} successfully completed TLS handshake.", feedbackConnection.name);

                                if (feedbackConnection.listener != null) {
                                    feedbackConnection.listener.handleConnectionSuccess(feedbackConnection);
                                }

                            } else {
                                log.debug("{} failed to complete TLS handshake with APNs feedback service.",
                                        feedbackConnection.name, handshakeFuture.cause());

                                connectFuture.channel().close();

                                if (feedbackConnection.listener != null) {
                                    feedbackConnection.listener.handleConnectionFailure(feedbackConnection,
                                            handshakeFuture.cause());
                                }
                            }
                        }
                    });
                } catch (NullPointerException e) {
                    log.warn("{} failed to get SSL handler and could not wait for a TLS handshake.",
                            feedbackConnection.name);

                    connectFuture.channel().close();

                    if (feedbackConnection.listener != null) {
                        feedbackConnection.listener.handleConnectionFailure(feedbackConnection, e);
                    }
                }
            } else {
                log.debug("{} failed to connect to APNs feedback service.", feedbackConnection.name,
                        connectFuture.cause());

                if (feedbackConnection.listener != null) {
                    feedbackConnection.listener.handleConnectionFailure(feedbackConnection,
                            connectFuture.cause());
                }
            }
        }
    });
}

From source file:com.shbxs.netty.SecureChatServerHandler.java

License:Apache License

@Override
public void channelActive(final ChannelHandlerContext ctx) {//?

    //       System.out.println("channelactive!");

    // Once session is secured, send a greeting and register the channel to the global channel
    // list so the channel received the messages from others.
    //??// ww  w . j a v a 2  s. co  m
    ctx.pipeline().get(SslHandler.class).handshakeFuture()
            .addListener(new GenericFutureListener<Future<Channel>>() {
                @Override
                public void operationComplete(Future<Channel> future) throws Exception {
                    ctx.writeAndFlush("Welcome to " + InetAddress.getLocalHost().getHostName()
                            + " secure chat service!\n");
                    ctx.writeAndFlush("Your session is protected by "
                            + ctx.pipeline().get(SslHandler.class).engine().getSession().getCipherSuite()
                            + " cipher suite.\n");

                    channels.add(ctx.channel());
                }
            });
}

From source file:com.turo.pushy.apns.ApnsChannelFactory.java

License:Open Source License

ApnsChannelFactory(final SslContext sslContext, final ApnsSigningKey signingKey,
        final ProxyHandlerFactory proxyHandlerFactory, final int connectTimeoutMillis,
        final long idlePingIntervalMillis, final long gracefulShutdownTimeoutMillis,
        final Http2FrameLogger frameLogger, final InetSocketAddress apnsServerAddress,
        final EventLoopGroup eventLoopGroup) {

    this.sslContext = sslContext;

    if (this.sslContext instanceof ReferenceCounted) {
        ((ReferenceCounted) this.sslContext).retain();
    }//  w w w  .  jav a  2  s .  co m

    this.addressResolverGroup = proxyHandlerFactory == null
            ? new RoundRobinDnsAddressResolverGroup(
                    ClientChannelClassUtil.getDatagramChannelClass(eventLoopGroup),
                    DefaultDnsServerAddressStreamProvider.INSTANCE)
            : NoopAddressResolverGroup.INSTANCE;

    this.bootstrapTemplate = new Bootstrap();
    this.bootstrapTemplate.group(eventLoopGroup);
    this.bootstrapTemplate.option(ChannelOption.TCP_NODELAY, true);
    this.bootstrapTemplate.remoteAddress(apnsServerAddress);
    this.bootstrapTemplate.resolver(this.addressResolverGroup);

    if (connectTimeoutMillis > 0) {
        this.bootstrapTemplate.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeoutMillis);
    }

    this.bootstrapTemplate.handler(new ChannelInitializer<SocketChannel>() {

        @Override
        protected void initChannel(final SocketChannel channel) {
            final ChannelPipeline pipeline = channel.pipeline();

            if (proxyHandlerFactory != null) {
                pipeline.addFirst(proxyHandlerFactory.createProxyHandler());
            }

            final SslHandler sslHandler = sslContext.newHandler(channel.alloc());

            sslHandler.handshakeFuture().addListener(new GenericFutureListener<Future<Channel>>() {
                @Override
                public void operationComplete(final Future<Channel> handshakeFuture) {
                    if (handshakeFuture.isSuccess()) {
                        final String authority = channel.remoteAddress().getHostName();

                        final ApnsClientHandler.ApnsClientHandlerBuilder clientHandlerBuilder;

                        if (signingKey != null) {
                            clientHandlerBuilder = new TokenAuthenticationApnsClientHandler.TokenAuthenticationApnsClientHandlerBuilder()
                                    .signingKey(signingKey).authority(authority)
                                    .idlePingIntervalMillis(idlePingIntervalMillis);
                        } else {
                            clientHandlerBuilder = new ApnsClientHandler.ApnsClientHandlerBuilder()
                                    .authority(authority).idlePingIntervalMillis(idlePingIntervalMillis);
                        }

                        if (frameLogger != null) {
                            clientHandlerBuilder.frameLogger(frameLogger);
                        }

                        final ApnsClientHandler apnsClientHandler = clientHandlerBuilder.build();

                        if (gracefulShutdownTimeoutMillis > 0) {
                            apnsClientHandler.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis);
                        }

                        // TODO Use a named constant when https://github.com/netty/netty/pull/8683 is available
                        pipeline.addLast(new FlushConsolidationHandler(256, true));
                        pipeline.addLast(
                                new IdleStateHandler(idlePingIntervalMillis, 0, 0, TimeUnit.MILLISECONDS));
                        pipeline.addLast(apnsClientHandler);
                        pipeline.remove(ConnectionNegotiationErrorHandler.INSTANCE);

                        channel.attr(CHANNEL_READY_PROMISE_ATTRIBUTE_KEY).get().trySuccess(channel);
                    } else {
                        tryFailureAndLogRejectedCause(channel.attr(CHANNEL_READY_PROMISE_ATTRIBUTE_KEY).get(),
                                handshakeFuture.cause());
                    }
                }
            });

            pipeline.addLast(sslHandler);
            pipeline.addLast(ConnectionNegotiationErrorHandler.INSTANCE);
        }
    });
}

From source file:com.turo.pushy.apns.ApnsChannelFactory.java

License:Open Source License

/**
 * Creates and connects a new channel. The initial connection attempt may be delayed to accommodate exponential
 * back-off requirements./* w  w w.j  a va  2  s.c  om*/
 *
 * @param channelReadyPromise the promise to be notified when a channel has been created and connected to the APNs
 * server
 *
 * @return a future that will be notified once a channel has been created and connected to the APNs server
 */
@Override
public Future<Channel> create(final Promise<Channel> channelReadyPromise) {
    final long delay = this.currentDelaySeconds.get();

    channelReadyPromise.addListener(new GenericFutureListener<Future<Channel>>() {

        @Override
        public void operationComplete(final Future<Channel> future) {
            final long updatedDelay = future.isSuccess() ? 0
                    : Math.max(Math.min(delay * 2, MAX_CONNECT_DELAY_SECONDS), MIN_CONNECT_DELAY_SECONDS);

            ApnsChannelFactory.this.currentDelaySeconds.compareAndSet(delay, updatedDelay);
        }
    });

    this.bootstrapTemplate.config().group().schedule(new Runnable() {

        @Override
        public void run() {

            final Bootstrap bootstrap = ApnsChannelFactory.this.bootstrapTemplate.clone()
                    .channelFactory(new AugmentingReflectiveChannelFactory<>(
                            ClientChannelClassUtil.getSocketChannelClass(
                                    ApnsChannelFactory.this.bootstrapTemplate.config().group()),
                            CHANNEL_READY_PROMISE_ATTRIBUTE_KEY, channelReadyPromise));

            final ChannelFuture connectFuture = bootstrap.connect();

            connectFuture.addListener(new GenericFutureListener<ChannelFuture>() {

                @Override
                public void operationComplete(final ChannelFuture future) {
                    if (!future.isSuccess()) {
                        // This may seem spurious, but our goal here is to accurately report the cause of
                        // connection failure; if we just wait for connection closure, we won't be able to
                        // tell callers anything more specific about what went wrong.
                        tryFailureAndLogRejectedCause(channelReadyPromise, future.cause());
                    }
                }
            });

            connectFuture.channel().closeFuture().addListener(new GenericFutureListener<ChannelFuture>() {

                @Override
                public void operationComplete(final ChannelFuture future) {
                    // We always want to try to fail the "channel ready" promise if the connection closes; if it has
                    // already succeeded, this will have no effect.
                    channelReadyPromise.tryFailure(
                            new IllegalStateException("Channel closed before HTTP/2 preface completed."));
                }
            });

        }
    }, delay, TimeUnit.SECONDS);

    return channelReadyPromise;
}

From source file:com.turo.pushy.apns.ApnsChannelPool.java

License:Open Source License

/**
 * <p>Asynchronously acquires a channel from this channel pool. The acquired channel may be a pre-existing channel
 * stored in the pool or may be a new channel created on demand. If no channels are available and the pool is at
 * capacity, acquisition may be delayed until another caller releases a channel to the pool.</p>
 *
 * <p>When callers are done with a channel, they <em>must</em> release the channel back to the pool via the
 * {@link ApnsChannelPool#release(Channel)} method.</p>
 *
 * @return a {@code Future} that will be notified when a channel is available
 *
 * @see ApnsChannelPool#release(Channel)
 *//*from   w ww  . ja v  a2  s  .  c  om*/
Future<Channel> acquire() {
    final Promise<Channel> acquirePromise = new DefaultPromise<>(this.executor);

    if (this.executor.inEventLoop()) {
        this.acquireWithinEventExecutor(acquirePromise);
    } else {
        this.executor.submit(new Runnable() {
            @Override
            public void run() {
                ApnsChannelPool.this.acquireWithinEventExecutor(acquirePromise);
            }
        }).addListener(new GenericFutureListener() {
            @Override
            public void operationComplete(final Future future) throws Exception {
                if (!future.isSuccess()) {
                    acquirePromise.tryFailure(future.cause());
                }
            }
        });
    }

    return acquirePromise;
}

From source file:com.turo.pushy.apns.ApnsChannelPool.java

License:Open Source License

private void acquireWithinEventExecutor(final Promise<Channel> acquirePromise) {
    assert this.executor.inEventLoop();

    if (!this.isClosed) {
        // We always want to open new channels if we have spare capacity. Once the pool is full, we'll start looking
        // for idle, pre-existing channels.
        if (this.allChannels.size() + this.pendingCreateChannelFutures.size() < this.capacity) {
            final Future<Channel> createChannelFuture = this.channelFactory
                    .create(executor.<Channel>newPromise());
            this.pendingCreateChannelFutures.add(createChannelFuture);

            createChannelFuture.addListener(new GenericFutureListener<Future<Channel>>() {

                @Override// www. j  a  v  a  2s.c o  m
                public void operationComplete(final Future<Channel> future) {
                    ApnsChannelPool.this.pendingCreateChannelFutures.remove(createChannelFuture);

                    if (future.isSuccess()) {
                        final Channel channel = future.getNow();

                        ApnsChannelPool.this.allChannels.add(channel);
                        ApnsChannelPool.this.metricsListener.handleConnectionAdded();

                        acquirePromise.trySuccess(channel);
                    } else {
                        ApnsChannelPool.this.metricsListener.handleConnectionCreationFailed();

                        acquirePromise.tryFailure(future.cause());

                        // If we failed to open a connection, this is the end of the line for this acquisition
                        // attempt, and callers won't be able to release the channel (since they didn't get one
                        // in the first place). Move on to the next acquisition attempt if one is present.
                        ApnsChannelPool.this.handleNextAcquisition();
                    }
                }
            });
        } else {
            final Channel channelFromIdlePool = ApnsChannelPool.this.idleChannels.poll();

            if (channelFromIdlePool != null) {
                if (channelFromIdlePool.isActive()) {
                    acquirePromise.trySuccess(channelFromIdlePool);
                } else {
                    // The channel from the idle pool isn't usable; discard it and create a new one instead
                    this.discardChannel(channelFromIdlePool);
                    this.acquireWithinEventExecutor(acquirePromise);
                }
            } else {
                // We don't have any connections ready to go, and don't have any more capacity to create new
                // channels. Add this acquisition to the queue waiting for channels to become available.
                pendingAcquisitionPromises.add(acquirePromise);
            }
        }
    } else {
        acquirePromise.tryFailure(POOL_CLOSED_EXCEPTION);
    }
}