001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.reef.wake.remote.impl;
020
021import org.apache.reef.tang.annotations.Parameter;
022import org.apache.reef.wake.EStage;
023import org.apache.reef.wake.EventHandler;
024import org.apache.reef.wake.impl.StageManager;
025import org.apache.reef.wake.remote.*;
026import org.apache.reef.wake.remote.address.LocalAddressProvider;
027import org.apache.reef.wake.remote.address.LocalAddressProviderFactory;
028import org.apache.reef.wake.remote.ports.RangeTcpPortProvider;
029import org.apache.reef.wake.remote.ports.TcpPortProvider;
030import org.apache.reef.wake.remote.transport.Transport;
031import org.apache.reef.wake.remote.transport.netty.NettyMessagingTransport;
032
033import javax.inject.Inject;
034import java.net.InetSocketAddress;
035import java.util.concurrent.ExecutorService;
036import java.util.concurrent.Executors;
037import java.util.concurrent.TimeUnit;
038import java.util.concurrent.atomic.AtomicBoolean;
039import java.util.concurrent.atomic.AtomicInteger;
040import java.util.logging.Level;
041import java.util.logging.Logger;
042
043/**
044 * Default remote manager implementation
045 */
046public class DefaultRemoteManagerImplementation implements RemoteManager {
047
048  private static final Logger LOG = Logger.getLogger(HandlerContainer.class.getName());
049
050  private static final AtomicInteger counter = new AtomicInteger(0);
051
052  /**
053   * The timeout used for the execute running in close()
054   */
055  private static final long CLOSE_EXECUTOR_TIMEOUT = 10000; //ms
056  private final AtomicBoolean closed = new AtomicBoolean(false);
057  private final String name;
058  private final Transport transport;
059  private final RemoteSenderStage reSendStage;
060  private final EStage<TransportEvent> reRecvStage;
061  private final HandlerContainer handlerContainer;
062  private final RemoteSeqNumGenerator seqGen = new RemoteSeqNumGenerator();
063  private RemoteIdentifier myIdentifier;
064
065
066  /**
067   * @deprecated have an instance injected instead.
068   */
069  @Deprecated
070  public <T> DefaultRemoteManagerImplementation(
071      final String name,
072      final String hostAddress,
073      final int listeningPort,
074      final Codec<T> codec,
075      final EventHandler<Throwable> errorHandler,
076      final boolean orderingGuarantee,
077      final int numberOfTries,
078      final int retryTimeout) {
079    this(name,
080        hostAddress,
081        listeningPort,
082        codec,
083        errorHandler,
084        orderingGuarantee,
085        numberOfTries,
086        retryTimeout,
087        LocalAddressProviderFactory.getInstance(),
088        RangeTcpPortProvider.Default);
089
090  }
091
092  /**
093   * @deprecated have an instance injected instead.
094   */
095  @Deprecated
096  @Inject
097  public <T> DefaultRemoteManagerImplementation(
098      final @Parameter(RemoteConfiguration.ManagerName.class) String name,
099      final @Parameter(RemoteConfiguration.HostAddress.class) String hostAddress,
100      final @Parameter(RemoteConfiguration.Port.class) int listeningPort,
101      final @Parameter(RemoteConfiguration.MessageCodec.class) Codec<T> codec,
102      final @Parameter(RemoteConfiguration.ErrorHandler.class) EventHandler<Throwable> errorHandler,
103      final @Parameter(RemoteConfiguration.OrderingGuarantee.class) boolean orderingGuarantee,
104      final @Parameter(RemoteConfiguration.NumberOfTries.class) int numberOfTries,
105      final @Parameter(RemoteConfiguration.RetryTimeout.class) int retryTimeout,
106      final LocalAddressProvider localAddressProvider,
107      final TcpPortProvider tcpPortProvider) {
108
109    this.name = name;
110    this.handlerContainer = new HandlerContainer<>(name, codec);
111
112    this.reRecvStage = orderingGuarantee ?
113        new OrderedRemoteReceiverStage(this.handlerContainer, errorHandler) :
114        new RemoteReceiverStage(this.handlerContainer, errorHandler, 10);
115
116    final String host = "##UNKNOWN##".equals(hostAddress) ? localAddressProvider.getLocalAddress() : hostAddress;
117    this.transport = new NettyMessagingTransport(
118            host, listeningPort, this.reRecvStage, this.reRecvStage, numberOfTries, retryTimeout, tcpPortProvider);
119
120    this.handlerContainer.setTransport(this.transport);
121
122    this.myIdentifier = new SocketRemoteIdentifier(
123        (InetSocketAddress) this.transport.getLocalAddress());
124
125    this.reSendStage = new RemoteSenderStage(codec, this.transport, 10);
126
127    StageManager.instance().register(this);
128
129    LOG.log(Level.FINEST, "RemoteManager {0} instantiated id {1} counter {2} listening on {3}:{4}. Binding address provided by {5}",
130        new Object[]{this.name, this.myIdentifier, counter.incrementAndGet(),
131            this.transport.getLocalAddress().toString(),
132            this.transport.getListeningPort(), localAddressProvider}
133    );
134  }
135
136  /**
137   * Returns a proxy event handler for a remote identifier and a message type
138   */
139  @Override
140  public <T> EventHandler<T> getHandler(
141      final RemoteIdentifier destinationIdentifier, final Class<? extends T> messageType) {
142
143    if (LOG.isLoggable(Level.FINE)) {
144      LOG.log(Level.FINE, "RemoteManager: {0} destinationIdentifier: {1} messageType: {2}",
145          new Object[]{this.name, destinationIdentifier, messageType.getName()});
146    }
147
148    return new ProxyEventHandler<>(this.myIdentifier, destinationIdentifier,
149        "default", this.reSendStage.<T>getHandler(), this.seqGen);
150  }
151
152  /**
153   * Registers an event handler for a remote identifier and a message type and
154   * returns a subscription
155   */
156  @Override
157  public <T, U extends T> AutoCloseable registerHandler(
158      final RemoteIdentifier sourceIdentifier,
159      final Class<U> messageType, final EventHandler<T> theHandler) {
160    if (LOG.isLoggable(Level.FINE)) {
161      LOG.log(Level.FINE, "RemoteManager: {0} remoteid: {1} messageType: {2} handler: {3}",
162          new Object[]{this.name, sourceIdentifier, messageType.getName(),
163              theHandler.getClass().getName()});
164    }
165    return this.handlerContainer.registerHandler(sourceIdentifier, messageType, theHandler);
166  }
167
168  /**
169   * Registers an event handler for a message type and returns a subscription
170   */
171  @Override
172  public <T, U extends T> AutoCloseable registerHandler(
173      final Class<U> messageType, final EventHandler<RemoteMessage<T>> theHandler) {
174    if (LOG.isLoggable(Level.FINE)) {
175      LOG.log(Level.FINE, "RemoteManager: {0} messageType: {1} handler: {2}",
176          new Object[]{this.name, messageType.getName(), theHandler.getClass().getName()});
177    }
178    return this.handlerContainer.registerHandler(messageType, theHandler);
179  }
180
181  /**
182   * Registers an exception handler and returns a subscription
183   */
184  @Override
185  public AutoCloseable registerErrorHandler(final EventHandler<Exception> theHandler) {
186    if (LOG.isLoggable(Level.FINE)) {
187      LOG.log(Level.FINE, "RemoteManager: {0} handler: {1}",
188          new Object[]{this.name, theHandler.getClass().getName()});
189    }
190    return this.handlerContainer.registerErrorHandler(theHandler);
191  }
192
193  /**
194   * Returns my identifier
195   */
196  @Override
197  public RemoteIdentifier getMyIdentifier() {
198    return this.myIdentifier;
199  }
200
201  @Override
202  public void close() {
203    if (closed.compareAndSet(false, true)) {
204
205      LOG.log(Level.FINE, "RemoteManager: {0} Closing remote manager id: {1}",
206          new Object[]{this.name, this.myIdentifier});
207
208      final Runnable closeRunnable = new Runnable() {
209        @Override
210        public void run() {
211          try {
212            LOG.log(Level.FINE, "Closing sender stage {0}", myIdentifier);
213            reSendStage.close();
214            LOG.log(Level.FINE, "Closed the remote sender stage");
215          } catch (final Exception e) {
216            LOG.log(Level.SEVERE, "Unable to close the remote sender stage", e);
217          }
218
219          try {
220            LOG.log(Level.FINE, "Closing transport {0}", myIdentifier);
221            transport.close();
222            LOG.log(Level.FINE, "Closed the transport");
223          } catch (final Exception e) {
224            LOG.log(Level.SEVERE, "Unable to close the transport.", e);
225          }
226
227          try {
228            LOG.log(Level.FINE, "Closing receiver stage {0}", myIdentifier);
229            reRecvStage.close();
230            LOG.log(Level.FINE, "Closed the remote receiver stage");
231          } catch (final Exception e) {
232            LOG.log(Level.SEVERE, "Unable to close the remote receiver stage", e);
233          }
234        }
235
236      };
237
238      final ExecutorService closeExecutor = Executors.newSingleThreadExecutor();
239      closeExecutor.submit(closeRunnable);
240      closeExecutor.shutdown();
241      if (!closeExecutor.isShutdown()) {
242        LOG.log(Level.SEVERE, "close executor did not shutdown properly.");
243      }
244
245      final long endTime = System.currentTimeMillis() + CLOSE_EXECUTOR_TIMEOUT;
246      while (!closeExecutor.isTerminated()) {
247        try {
248          final long waitTime = endTime - System.currentTimeMillis();
249          closeExecutor.awaitTermination(waitTime, TimeUnit.MILLISECONDS);
250        } catch (final InterruptedException e) {
251          LOG.log(Level.FINE, "Interrupted", e);
252        }
253      }
254
255      if (closeExecutor.isTerminated()) {
256        LOG.log(Level.FINE, "Close executor terminated properly.");
257      } else {
258        LOG.log(Level.SEVERE, "Close executor did not terminate properly.");
259      }
260    }
261  }
262}