/* * JBoss, Home of Professional Open Source. * Copyright 2015, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.remoting3; import static org.xnio.IoUtils.safeClose; import java.io.IOException; import java.util.function.Function; import org.xnio.FinishedIoFuture; import org.xnio.FutureResult; import org.xnio.IoFuture; import org.xnio.OptionMap; /** * A handle for helping service protocol providers to create and maintain a single channel per connection. * * @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a> */ public final class ClientServiceHandle<T> { private final Attachments.Key<IoFuture<T>> key; private final String serviceName; private final Function<Channel, IoFuture<T>> constructor; /** * Construct a new instance. Only one instance should be constructed per service; instances may be safely * cached in {@code static} fields. * * @param serviceName the service name (may not be {@code null}) * @param constructor the service future construction operation */ @SuppressWarnings("unchecked") public ClientServiceHandle(final String serviceName, final Function<Channel, IoFuture<T>> constructor) { key = (Attachments.Key<IoFuture<T>>) (Object) new Attachments.Key<>(IoFuture.class); this.serviceName = serviceName; this.constructor = constructor; } /** * Get or establish the future client service for the given connection. * * @param connection the connection * @param optionMap the service options * @return the future service instance */ public IoFuture<T> getClientService(final Connection connection, OptionMap optionMap) { final Attachments attachments = connection.getAttachments(); // check for prior IoFuture<T> existing = attachments.getAttachment(key); if (existing != null) { return existing; } // create new future result, try to attach it final FutureResult<T> futureResult = new FutureResult<>(connection.getEndpoint().getXnioWorker()); final IoFuture<T> future = futureResult.getIoFuture(); existing = attachments.attachIfAbsent(key, future); if (existing != null) { return existing; } // open the channel and create notifiers to trigger user construction operation final IoFuture<Channel> futureChannel = connection.openChannel(serviceName, optionMap); futureChannel.addNotifier(new IoFuture.HandlingNotifier<Channel, FutureResult<T>>() { public void handleCancelled(final FutureResult<T> futureResult) { futureResult.setCancelled(); attachments.removeAttachment(key, futureResult.getIoFuture()); } public void handleFailed(final IOException exception, final FutureResult<T> futureResult) { futureResult.setException(exception); attachments.removeAttachment(key, futureResult.getIoFuture()); } public void handleDone(final Channel channel, final FutureResult<T> futureResult) { final IoFuture<T> nextFuture = constructor.apply(channel); nextFuture.addNotifier(new IoFuture.HandlingNotifier<T, FutureResult<T>>() { public void handleCancelled(final FutureResult<T> futureResult) { safeClose(channel); futureResult.setCancelled(); attachments.removeAttachment(key, futureResult.getIoFuture()); } public void handleFailed(final IOException exception, final FutureResult<T> futureResult) { safeClose(channel); futureResult.setException(exception); attachments.removeAttachment(key, futureResult.getIoFuture()); } public void handleDone(final T result, final FutureResult<T> futureResult) { // Publish result futureResult.setResult(result); // Optimize overall attachments.replaceAttachment(key, futureResult.getIoFuture(), new FinishedIoFuture<T>(result)); // Remove on close channel.addCloseHandler((closed, exception) -> attachments.removeAttachment(key)); } }, futureResult); // make sure cancel requests now pass up to the service future futureResult.addCancelHandler(nextFuture); } }, futureResult); // make sure cancel requests pass up to the channel open request futureResult.addCancelHandler(futureChannel); return future; } }