Java tutorial
/* * Copyright 2013 Andrew Kroh * * 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.andrewkroh.cisco.xmlservices; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.EventLoopGroup; import io.netty.handler.codec.http.HttpRequest; import io.netty.util.AttributeKey; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.andrewkroh.cisco.phoneinventory.IpPhone; import com.google.common.util.concurrent.SettableFuture; /** * Listens for successful client channel connection and then writes the * {@link HttpRequest} to the channel. * * @author akroh */ class ChannelConnectListener<T> implements ChannelFutureListener { /** * SLF4J logger for this class. */ private static final Logger LOGGER = LoggerFactory.getLogger(ChannelConnectListener.class); /** * HTTP request to write to the channel after it is connected. */ private final HttpRequest httpRequest; /** * {@code AttributeKey} used to put the {@code IpPhone} object into the * {@code Channel}. */ private final AttributeKey<IpPhone> phoneAttributeKey; /** * {@code IpPhone} to store into the {@code Channel}. */ private final IpPhone phone; /** * {@code AttributeKey} used to put the {@link SettableFuture} object into * the {@code Channel} using {@link Channel#attr(AttributeKey)}. The * {@code SettableFuture} is used to return the response from the * asynchronous call. */ private final AttributeKey<SettableFuture<T>> responseFutureAttributeKey; /** * {@link SettableFuture} that is used to return the asynchronous response * to the caller or to indicate that an exception occurred. The client can * cancel the request, therefore this class should check if the * {@code Future} has been cancelled when it has the opportunity to * terminate the operation. */ private final SettableFuture<T> responseFuture; /** * {@link ScheduledExecutorService} used to schedule the timeout of the * {@code responseFuture} and close the {@code Channel} if the response * has not been received. */ private final ScheduledExecutorService eventLoopExecutor; /** * Amount of time to wait before timing out the {@code responseFuture} and * closing the {@code Channel} */ private final long responseTimeoutMs; /** * Constructs a new {@code ChannelFutureListener} that should be added as a * listener to the {@link Channel#connect(java.net.SocketAddress)} * operation. When that operation completes this class's * {@link #operationComplete(ChannelFuture)} is invoked. * * <p/> * Upon completion of the {@code connect} this will submit the HTTP request * using {@link Channel#writeAndFlush(Object)}. * * @param httpRequest * {@code HttpRequest} to write to the channel after the channel * is connected * @param phoneAttributeKey * {@code AttributeKey} used to store the {@code IpPhone} * with the channel * @param phone * {@code IpPhone} to store with the channel * @param responseFutureAttributeKey * {@code AttributeKey} used to store the {@code SettableFuture} * with the channel * @param responseFuture * {@code SettableFuture} to store with the channel * @param eventLoopExecutor * {@code ScheduledExecutorService} that will be used to schedule * an automatic timeout if the response hasn't been received, * this should be the {@link EventLoopGroup} * @param responseTimeoutMs * amount of time in milliseconds before the * {@code responseFuture} will be timed out and the * {@code Channel} closed */ public ChannelConnectListener(HttpRequest httpRequest, AttributeKey<IpPhone> phoneAttributeKey, IpPhone phone, AttributeKey<SettableFuture<T>> responseFutureAttributeKey, SettableFuture<T> responseFuture, ScheduledExecutorService eventLoopExecutor, long responseTimeoutMs) { this.httpRequest = httpRequest; this.phoneAttributeKey = phoneAttributeKey; this.phone = phone; this.responseFutureAttributeKey = responseFutureAttributeKey; this.responseFuture = responseFuture; this.eventLoopExecutor = eventLoopExecutor; this.responseTimeoutMs = responseTimeoutMs; } @Override public void operationComplete(ChannelFuture future) throws Exception { LOGGER.debug("connect() complete, status: " + future); if (responseFuture.isCancelled()) { future.channel().close(); return; } if (future.isSuccess()) { final Channel channel = future.channel(); channel.attr(phoneAttributeKey).set(phone); channel.attr(responseFutureAttributeKey).set(responseFuture); // Timeout the task if it does not complete: eventLoopExecutor.schedule(new Runnable() { @Override public void run() { if (!responseFuture.isDone()) { responseFuture.cancel(false); channel.close(); } } }, responseTimeoutMs, TimeUnit.MILLISECONDS); channel.writeAndFlush(httpRequest).addListener(new ChannelWriteFuture<T>(responseFuture)); } else { responseFuture.setException(future.cause()); future.channel().close(); } } }