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.runtime.common.launch;
020
021import org.apache.reef.runtime.common.evaluator.PIDStoreStartHandler;
022import org.apache.reef.runtime.common.launch.parameters.ClockConfigurationPath;
023import org.apache.reef.runtime.common.launch.parameters.ErrorHandlerRID;
024import org.apache.reef.runtime.common.launch.parameters.LaunchID;
025import org.apache.reef.tang.*;
026import org.apache.reef.tang.annotations.Name;
027import org.apache.reef.tang.annotations.NamedParameter;
028import org.apache.reef.tang.annotations.Parameter;
029import org.apache.reef.tang.formats.ConfigurationSerializer;
030import org.apache.reef.util.REEFVersion;
031import org.apache.reef.wake.profiler.WakeProfiler;
032import org.apache.reef.wake.remote.RemoteConfiguration;
033import org.apache.reef.wake.time.Clock;
034
035import javax.inject.Inject;
036import java.io.File;
037import java.io.FileNotFoundException;
038import java.io.IOException;
039import java.util.logging.Level;
040import java.util.logging.Logger;
041
042/**
043 * This encapsulates processes started by REEF.
044 */
045public final class LaunchClass implements AutoCloseable, Runnable {
046
047  private static final Logger LOG = Logger.getLogger(LaunchClass.class.getName());
048  private final String launchID;
049  private final String errorHandlerID;
050  private final String evaluatorConfigurationPath;
051  private final boolean isProfilingEnabled;
052  private final REEFErrorHandler errorHandler;
053  private final ConfigurationSerializer configurationSerializer;
054  private WakeProfiler profiler;
055
056  @Inject
057  LaunchClass(final REEFUncaughtExceptionHandler uncaughtExceptionHandler,
058              final REEFErrorHandler errorHandler,
059              final @Parameter(LaunchID.class) String launchID,
060              final @Parameter(ErrorHandlerRID.class) String errorHandlerID,
061              final @Parameter(ClockConfigurationPath.class) String evaluatorConfigurationPath,
062              final @Parameter(ProfilingEnabled.class) boolean enableProfiling,
063              final ConfigurationSerializer configurationSerializer,
064              final REEFVersion reefVersion) {
065    reefVersion.logVersion();
066    this.launchID = launchID;
067    this.errorHandlerID = errorHandlerID;
068    this.evaluatorConfigurationPath = evaluatorConfigurationPath;
069    this.isProfilingEnabled = enableProfiling;
070    this.errorHandler = errorHandler;
071    this.configurationSerializer = configurationSerializer;
072
073
074    // Registering a default exception handler. It sends every exception to the upstream RemoteManager
075    Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
076
077
078    if (isProfilingEnabled) {
079      this.profiler = new WakeProfiler();
080      ProfilingStopHandler.setProfiler(profiler); // TODO: This probably should be bound via Tang.
081    }
082    LOG.log(Level.FINE, "Instantiated LaunchClass");
083  }
084
085  /**
086   * Loads the client and resource manager configuration files from disk.
087   */
088  private Configuration getClockConfiguration() {
089    return Configurations.merge(readConfigurationFromDisk(), getStaticClockConfiguration());
090  }
091
092
093  private Configuration readConfigurationFromDisk() {
094    LOG.log(Level.FINEST, "Loading configuration file: {0}", this.evaluatorConfigurationPath);
095
096    final File evaluatorConfigFile = new File(this.evaluatorConfigurationPath);
097
098    if (!evaluatorConfigFile.exists()) {
099      final String message = "The configuration file " + this.evaluatorConfigurationPath +
100          "doesn't exist. This points to an issue in the job submission.";
101      fail(message, new FileNotFoundException());
102      throw new RuntimeException(message);
103    } else if (!evaluatorConfigFile.canRead()) {
104      final String message = "The configuration file " + this.evaluatorConfigurationPath + " exists, but can't be read";
105      fail(message, new IOException());
106      throw new RuntimeException(message);
107    } else {
108      try {
109        return this.configurationSerializer.fromFile(evaluatorConfigFile);
110      } catch (final IOException e) {
111        final String message = "Unable to parse the configuration file " + this.evaluatorConfigurationPath;
112        fail(message, e);
113        throw new RuntimeException(message, e);
114      }
115    }
116  }
117
118  /**
119   * @return the part of the clock configuration *not* read from disk.
120   */
121  private Configuration getStaticClockConfiguration() {
122    final JavaConfigurationBuilder builder = Tang.Factory.getTang().newConfigurationBuilder()
123        .bindNamedParameter(LaunchID.class, this.launchID)
124        .bindNamedParameter(ErrorHandlerRID.class, this.errorHandlerID)
125        .bindSetEntry(Clock.StartHandler.class, PIDStoreStartHandler.class)
126        .bindNamedParameter(RemoteConfiguration.ErrorHandler.class, REEFErrorHandler.class)
127        .bindNamedParameter(RemoteConfiguration.ManagerName.class, "REEF_LAUNCHER")
128        .bindNamedParameter(RemoteConfiguration.MessageCodec.class, REEFMessageCodec.class);
129    if (this.isProfilingEnabled) {
130      builder.bindSetEntry(Clock.StopHandler.class, ProfilingStopHandler.class);
131    }
132    return builder.build();
133  }
134
135  /**
136   * Instantiates the clock.
137   *
138   * @return a clock object.
139   */
140  private Clock getClock() {
141    try {
142      final Injector clockInjector = Tang.Factory.getTang().newInjector(this.getClockConfiguration());
143      if (isProfilingEnabled) {
144        clockInjector.bindAspect(profiler);
145      }
146      return clockInjector.getInstance(Clock.class);
147    } catch (final Throwable ex) {
148      fail("Unable to instantiate the clock", ex);
149      throw new RuntimeException("Unable to instantiate the clock", ex);
150    }
151  }
152
153  /**
154   * Starts the Clock.
155   * This blocks until the clock returns.
156   */
157  @Override
158  public void run() {
159    LOG.entering(this.getClass().getName(), "run", "Starting the clock");
160    try {
161      this.getClock().run();
162    } catch (final Throwable t) {
163      fail("Fatal exception while executing the clock", t);
164    }
165    LOG.exiting(this.getClass().getName(), "run", "Clock terminated");
166  }
167
168  /**
169   * Closes the remote manager managed by this class.
170   *
171   * @throws Exception
172   */
173  @Override
174  public void close() throws Exception {
175    LOG.entering(this.getClass().getName(), "close");
176    this.errorHandler.close(); // Also closes the remoteManager
177    LOG.exiting(this.getClass().getName(), "close");
178  }
179
180  private void fail(final String message, final Throwable throwable) {
181    this.errorHandler.onNext(new Exception(message, throwable));
182  }
183
184  @NamedParameter(doc = "If true, profiling will be enabled", short_name = "profiling", default_value = "false")
185  public final static class ProfilingEnabled implements Name<Boolean> {
186  }
187}