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.driver.evaluator; 020 021import org.apache.reef.annotations.audience.DriverSide; 022import org.apache.reef.annotations.audience.Private; 023import org.apache.reef.tang.ConfigurationProvider; 024import org.apache.reef.driver.context.ActiveContext; 025import org.apache.reef.driver.context.FailedContext; 026import org.apache.reef.driver.evaluator.AllocatedEvaluator; 027import org.apache.reef.driver.evaluator.EvaluatorDescriptor; 028import org.apache.reef.driver.evaluator.EvaluatorType; 029import org.apache.reef.driver.parameters.EvaluatorConfigurationProviders; 030import org.apache.reef.driver.task.FailedTask; 031import org.apache.reef.exception.EvaluatorException; 032import org.apache.reef.exception.EvaluatorKilledByResourceManagerException; 033import org.apache.reef.io.naming.Identifiable; 034import org.apache.reef.proto.EvaluatorRuntimeProtocol; 035import org.apache.reef.proto.ReefServiceProtos; 036import org.apache.reef.runtime.common.DriverRestartCompleted; 037import org.apache.reef.runtime.common.driver.DriverStatusManager; 038import org.apache.reef.runtime.common.driver.api.ResourceLaunchEvent; 039import org.apache.reef.runtime.common.driver.api.ResourceReleaseEventImpl; 040import org.apache.reef.runtime.common.driver.api.ResourceLaunchHandler; 041import org.apache.reef.runtime.common.driver.api.ResourceReleaseHandler; 042import org.apache.reef.runtime.common.driver.context.ContextControlHandler; 043import org.apache.reef.runtime.common.driver.context.ContextRepresenters; 044import org.apache.reef.runtime.common.driver.idle.EventHandlerIdlenessSource; 045import org.apache.reef.runtime.common.driver.resourcemanager.ResourceStatusEvent; 046import org.apache.reef.runtime.common.driver.task.TaskRepresenter; 047import org.apache.reef.runtime.common.utils.ExceptionCodec; 048import org.apache.reef.runtime.common.utils.RemoteManager; 049import org.apache.reef.tang.annotations.Name; 050import org.apache.reef.tang.annotations.NamedParameter; 051import org.apache.reef.tang.annotations.Parameter; 052import org.apache.reef.tang.formats.ConfigurationSerializer; 053import org.apache.reef.util.Optional; 054import org.apache.reef.util.logging.LoggingScopeFactory; 055import org.apache.reef.wake.EventHandler; 056import org.apache.reef.wake.remote.RemoteMessage; 057import org.apache.reef.wake.time.Clock; 058import org.apache.reef.wake.time.event.Alarm; 059 060import javax.inject.Inject; 061import java.io.File; 062import java.util.List; 063import java.util.Set; 064import java.util.logging.Level; 065import java.util.logging.Logger; 066 067/** 068 * Manages a single Evaluator instance including all lifecycle instances: 069 * (AllocatedEvaluator, CompletedEvaluator, FailedEvaluator). 070 * <p/> 071 * A (periodic) heartbeat channel is established EvaluatorRuntime -> EvaluatorManager. 072 * The EvaluatorRuntime will (periodically) send (status) messages to the EvaluatorManager using this 073 * heartbeat channel. 074 * <p/> 075 * A (push-based) EventHandler channel is established EvaluatorManager -> EvaluatorRuntime. 076 * The EvaluatorManager uses this to forward Driver messages, launch Tasks, and initiate 077 * control information (e.g., shutdown, suspend). 078 */ 079@Private 080@DriverSide 081public final class EvaluatorManager implements Identifiable, AutoCloseable { 082 083 private final static Logger LOG = Logger.getLogger(EvaluatorManager.class.getName()); 084 085 private final EvaluatorHeartBeatSanityChecker sanityChecker = new EvaluatorHeartBeatSanityChecker(); 086 private final Clock clock; 087 private final ResourceReleaseHandler resourceReleaseHandler; 088 private final ResourceLaunchHandler resourceLaunchHandler; 089 private final String evaluatorId; 090 private final EvaluatorDescriptorImpl evaluatorDescriptor; 091 private final ContextRepresenters contextRepresenters; 092 private final EvaluatorMessageDispatcher messageDispatcher; 093 private final EvaluatorControlHandler evaluatorControlHandler; 094 private final ContextControlHandler contextControlHandler; 095 private final EvaluatorStatusManager stateManager; 096 private final ExceptionCodec exceptionCodec; 097 private final DriverStatusManager driverStatusManager; 098 private final EventHandlerIdlenessSource idlenessSource; 099 private final LoggingScopeFactory loggingScopeFactory; 100 101 102 // Mutable fields 103 private Optional<TaskRepresenter> task = Optional.empty(); 104 private boolean isResourceReleased = false; 105 106 @Inject 107 private EvaluatorManager( 108 final Clock clock, 109 final RemoteManager remoteManager, 110 final ResourceReleaseHandler resourceReleaseHandler, 111 final ResourceLaunchHandler resourceLaunchHandler, 112 final @Parameter(EvaluatorIdentifier.class) String evaluatorId, 113 final @Parameter(EvaluatorDescriptorName.class) EvaluatorDescriptorImpl evaluatorDescriptor, 114 final ContextRepresenters contextRepresenters, 115 final ConfigurationSerializer configurationSerializer, 116 final EvaluatorMessageDispatcher messageDispatcher, 117 final EvaluatorControlHandler evaluatorControlHandler, 118 final ContextControlHandler contextControlHandler, 119 final EvaluatorStatusManager stateManager, 120 final DriverStatusManager driverStatusManager, 121 final ExceptionCodec exceptionCodec, 122 final EventHandlerIdlenessSource idlenessSource, 123 final LoggingScopeFactory loggingScopeFactory, 124 final @Parameter(EvaluatorConfigurationProviders.class) Set<ConfigurationProvider> evaluatorConfigurationProviders) { 125 this.contextRepresenters = contextRepresenters; 126 this.idlenessSource = idlenessSource; 127 LOG.log(Level.FINEST, "Instantiating 'EvaluatorManager' for evaluator: {0}", evaluatorId); 128 this.clock = clock; 129 this.resourceReleaseHandler = resourceReleaseHandler; 130 this.resourceLaunchHandler = resourceLaunchHandler; 131 this.evaluatorId = evaluatorId; 132 this.evaluatorDescriptor = evaluatorDescriptor; 133 134 this.messageDispatcher = messageDispatcher; 135 this.evaluatorControlHandler = evaluatorControlHandler; 136 this.contextControlHandler = contextControlHandler; 137 this.stateManager = stateManager; 138 this.driverStatusManager = driverStatusManager; 139 this.exceptionCodec = exceptionCodec; 140 this.loggingScopeFactory = loggingScopeFactory; 141 142 final AllocatedEvaluator allocatedEvaluator = 143 new AllocatedEvaluatorImpl(this, remoteManager.getMyIdentifier(), configurationSerializer, getJobIdentifier(), loggingScopeFactory, evaluatorConfigurationProviders); 144 LOG.log(Level.FINEST, "Firing AllocatedEvaluator event for Evaluator with ID [{0}]", evaluatorId); 145 this.messageDispatcher.onEvaluatorAllocated(allocatedEvaluator); 146 LOG.log(Level.FINEST, "Instantiated 'EvaluatorManager' for evaluator: [{0}]", this.getId()); 147 } 148 149 /** 150 * Get the id of current job/application 151 */ 152 public static String getJobIdentifier() { 153 // TODO: currently we obtain the job id directly by parsing execution (container) directory path 154 // #845 is open to get the id from RM properly 155 for (File directory = new File(System.getProperty("user.dir")); 156 directory != null; directory = directory.getParentFile()) { 157 final String currentDirectoryName = directory.getName(); 158 if (currentDirectoryName.toLowerCase().contains("application_")) { 159 return currentDirectoryName; 160 } 161 } 162 // cannot find a directory that contains application_, presumably we are on local runtime 163 // again, this is a hack for now, we need #845 as a proper solution 164 return "REEF_LOCAL_RUNTIME"; 165 } 166 167 private static boolean isDoneOrFailedOrKilled(final ResourceStatusEvent resourceStatusEvent) { 168 return resourceStatusEvent.getState() == ReefServiceProtos.State.DONE || 169 resourceStatusEvent.getState() == ReefServiceProtos.State.FAILED || 170 resourceStatusEvent.getState() == ReefServiceProtos.State.KILLED; 171 } 172 173 @Override 174 public String getId() { 175 return this.evaluatorId; 176 } 177 178 public void setType(final EvaluatorType type) { 179 this.evaluatorDescriptor.setType(type); 180 } 181 182 public EvaluatorDescriptor getEvaluatorDescriptor() { 183 return this.evaluatorDescriptor; 184 } 185 186 @Override 187 public void close() { 188 synchronized (this.evaluatorDescriptor) { 189 if (this.stateManager.isRunning()) { 190 LOG.log(Level.WARNING, "Dirty shutdown of running evaluator id[{0}]", getId()); 191 try { 192 // Killing the evaluator means that it doesn't need to send a confirmation; it just dies. 193 final EvaluatorRuntimeProtocol.EvaluatorControlProto evaluatorControlProto = 194 EvaluatorRuntimeProtocol.EvaluatorControlProto.newBuilder() 195 .setTimestamp(System.currentTimeMillis()) 196 .setIdentifier(getId()) 197 .setKillEvaluator(EvaluatorRuntimeProtocol.KillEvaluatorProto.newBuilder().build()) 198 .build(); 199 sendEvaluatorControlMessage(evaluatorControlProto); 200 } finally { 201 this.stateManager.setKilled(); 202 } 203 } 204 205 206 if (!this.isResourceReleased) { 207 this.isResourceReleased = true; 208 try { 209 /* We need to wait awhile before returning the container to the RM in order to 210 * give the EvaluatorRuntime (and Launcher) time to cleanly exit. */ 211 this.clock.scheduleAlarm(100, new EventHandler<Alarm>() { 212 @Override 213 public void onNext(final Alarm alarm) { 214 EvaluatorManager.this.resourceReleaseHandler.onNext( 215 ResourceReleaseEventImpl.newBuilder() 216 .setIdentifier(EvaluatorManager.this.evaluatorId).build() 217 ); 218 } 219 }); 220 } catch (final IllegalStateException e) { 221 LOG.log(Level.WARNING, "Force resource release because the client closed the clock.", e); 222 EvaluatorManager.this.resourceReleaseHandler.onNext( 223 ResourceReleaseEventImpl.newBuilder() 224 .setIdentifier(EvaluatorManager.this.evaluatorId).build() 225 ); 226 } 227 } 228 } 229 this.idlenessSource.check(); 230 } 231 232 /** 233 * Return true if the state is DONE, FAILED, or KILLED, 234 * <em>and</em> there are no messages queued or in processing. 235 */ 236 public boolean isClosed() { 237 return this.messageDispatcher.isEmpty() && 238 (this.stateManager.isDoneOrFailedOrKilled()); 239 } 240 241 /** 242 * EvaluatorException will trigger is FailedEvaluator and state transition to FAILED 243 * 244 * @param exception on the EvaluatorRuntime 245 */ 246 public void onEvaluatorException(final EvaluatorException exception) { 247 synchronized (this.evaluatorDescriptor) { 248 if (this.stateManager.isDoneOrFailedOrKilled()) { 249 LOG.log(Level.FINE, "Ignoring an exception receivedfor Evaluator {0} which is already in state {1}.", 250 new Object[]{this.getId(), this.stateManager}); 251 return; 252 } 253 254 LOG.log(Level.WARNING, "Failed evaluator: " + getId(), exception); 255 256 try { 257 258 final List<FailedContext> failedContextList = this.contextRepresenters.getFailedContextsForEvaluatorFailure(); 259 260 final Optional<FailedTask> failedTaskOptional; 261 if (this.task.isPresent()) { 262 final String taskId = this.task.get().getId(); 263 final Optional<ActiveContext> evaluatorContext = Optional.empty(); 264 final Optional<byte[]> bytes = Optional.empty(); 265 final Optional<Throwable> taskException = Optional.<Throwable>of(new Exception("Evaluator crash")); 266 final String message = "Evaluator crash"; 267 final Optional<String> description = Optional.empty(); 268 final FailedTask failedTask = new FailedTask(taskId, message, description, taskException, bytes, evaluatorContext); 269 failedTaskOptional = Optional.of(failedTask); 270 } else { 271 failedTaskOptional = Optional.empty(); 272 } 273 274 275 this.messageDispatcher.onEvaluatorFailed(new FailedEvaluatorImpl(exception, failedContextList, failedTaskOptional, this.evaluatorId)); 276 277 } catch (final Exception e) { 278 LOG.log(Level.SEVERE, "Exception while handling FailedEvaluator", e); 279 } finally { 280 this.stateManager.setFailed(); 281 close(); 282 } 283 } 284 } 285 286 public void onEvaluatorHeartbeatMessage( 287 final RemoteMessage<EvaluatorRuntimeProtocol.EvaluatorHeartbeatProto> evaluatorHeartbeatProtoRemoteMessage) { 288 289 final EvaluatorRuntimeProtocol.EvaluatorHeartbeatProto evaluatorHeartbeatProto = 290 evaluatorHeartbeatProtoRemoteMessage.getMessage(); 291 LOG.log(Level.FINEST, "Evaluator heartbeat: {0}", evaluatorHeartbeatProto); 292 293 synchronized (this.evaluatorDescriptor) { 294 if (this.stateManager.isDoneOrFailedOrKilled()) { 295 LOG.log(Level.FINE, "Ignoring an heartbeat received for Evaluator {0} which is already in state {1}.", 296 new Object[]{this.getId(), this.stateManager}); 297 return; 298 } 299 300 this.sanityChecker.check(evaluatorId, evaluatorHeartbeatProto.getTimestamp()); 301 final String evaluatorRID = evaluatorHeartbeatProtoRemoteMessage.getIdentifier().toString(); 302 303 // first message from a running evaluator trying to re-establish communications 304 if (evaluatorHeartbeatProto.getRecovery()) { 305 this.evaluatorControlHandler.setRemoteID(evaluatorRID); 306 this.stateManager.setRunning(); 307 308 this.driverStatusManager.oneContainerRecovered(); 309 final int numRecoveredContainers = this.driverStatusManager.getNumRecoveredContainers(); 310 311 LOG.log(Level.FINE, "Received recovery heartbeat from evaluator {0}.", this.evaluatorId); 312 final int expectedEvaluatorsNumber = this.driverStatusManager.getNumPreviousContainers(); 313 314 if (numRecoveredContainers > expectedEvaluatorsNumber) { 315 LOG.log(Level.SEVERE, "expecting only [{0}] recovered evaluators, but [{1}] evaluators have checked in.", 316 new Object[]{expectedEvaluatorsNumber, numRecoveredContainers}); 317 throw new RuntimeException("More then expected number of evaluators are checking in during recovery."); 318 } else if (numRecoveredContainers == expectedEvaluatorsNumber) { 319 LOG.log(Level.INFO, "All [{0}] expected evaluators have checked in. Recovery completed.", expectedEvaluatorsNumber); 320 this.driverStatusManager.setRestartCompleted(); 321 this.messageDispatcher.OnDriverRestartCompleted(new DriverRestartCompleted(System.currentTimeMillis())); 322 } else { 323 LOG.log(Level.INFO, "expecting [{0}] recovered evaluators, [{1}] evaluators have checked in.", 324 new Object[]{expectedEvaluatorsNumber, numRecoveredContainers}); 325 } 326 } 327 328 // If this is the first message from this Evaluator, register it. 329 if (this.stateManager.isSubmitted()) { 330 this.evaluatorControlHandler.setRemoteID(evaluatorRID); 331 this.stateManager.setRunning(); 332 LOG.log(Level.FINEST, "Evaluator {0} is running", this.evaluatorId); 333 } 334 335 // Process the Evaluator status message 336 if (evaluatorHeartbeatProto.hasEvaluatorStatus()) { 337 this.onEvaluatorStatusMessage(evaluatorHeartbeatProto.getEvaluatorStatus()); 338 } 339 340 // Process the Context status message(s) 341 final boolean informClientOfNewContexts = !evaluatorHeartbeatProto.hasTaskStatus(); 342 this.contextRepresenters.onContextStatusMessages(evaluatorHeartbeatProto.getContextStatusList(), 343 informClientOfNewContexts); 344 345 // Process the Task status message 346 if (evaluatorHeartbeatProto.hasTaskStatus()) { 347 this.onTaskStatusMessage(evaluatorHeartbeatProto.getTaskStatus()); 348 } 349 LOG.log(Level.FINE, "DONE with evaluator heartbeat from Evaluator {0}", this.getId()); 350 } 351 } 352 353 /** 354 * Process a evaluator status message. 355 * 356 * @param message 357 */ 358 private synchronized void onEvaluatorStatusMessage(final ReefServiceProtos.EvaluatorStatusProto message) { 359 360 switch (message.getState()) { 361 case DONE: 362 this.onEvaluatorDone(message); 363 break; 364 case FAILED: 365 this.onEvaluatorFailed(message); 366 break; 367 case INIT: 368 case KILLED: 369 case RUNNING: 370 case SUSPEND: 371 break; 372 } 373 } 374 375 /** 376 * Process an evaluator message that indicates that the evaluator shut down cleanly. 377 * 378 * @param message 379 */ 380 private synchronized void onEvaluatorDone(final ReefServiceProtos.EvaluatorStatusProto message) { 381 assert (message.getState() == ReefServiceProtos.State.DONE); 382 LOG.log(Level.FINEST, "Evaluator {0} done.", getId()); 383 this.stateManager.setDone(); 384 this.messageDispatcher.onEvaluatorCompleted(new CompletedEvaluatorImpl(this.evaluatorId)); 385 close(); 386 } 387 388 /** 389 * Process an evaluator message that indicates a crash. 390 * 391 * @param evaluatorStatusProto 392 */ 393 private synchronized void onEvaluatorFailed(final ReefServiceProtos.EvaluatorStatusProto evaluatorStatusProto) { 394 assert (evaluatorStatusProto.getState() == ReefServiceProtos.State.FAILED); 395 final EvaluatorException evaluatorException; 396 if (evaluatorStatusProto.hasError()) { 397 final Optional<Throwable> exception = this.exceptionCodec.fromBytes(evaluatorStatusProto.getError().toByteArray()); 398 if (exception.isPresent()) { 399 evaluatorException = new EvaluatorException(getId(), exception.get()); 400 } else { 401 evaluatorException = new EvaluatorException(getId(), new Exception("Exception sent, but can't be deserialized")); 402 } 403 } else { 404 evaluatorException = new EvaluatorException(getId(), new Exception("No exception sent")); 405 } 406 onEvaluatorException(evaluatorException); 407 } 408 409 public void onResourceLaunch(final ResourceLaunchEvent resourceLaunchEvent) { 410 synchronized (this.evaluatorDescriptor) { 411 if (this.stateManager.isAllocated()) { 412 this.stateManager.setSubmitted(); 413 this.resourceLaunchHandler.onNext(resourceLaunchEvent); 414 } else { 415 throw new RuntimeException("Evaluator manager expected " + EvaluatorState.ALLOCATED + 416 " state but instead is in state " + this.stateManager); 417 } 418 } 419 } 420 421 /** 422 * Packages the ContextControlProto in an EvaluatorControlProto and forward it to the EvaluatorRuntime 423 * 424 * @param contextControlProto message contains context control info. 425 */ 426 public void sendContextControlMessage(final EvaluatorRuntimeProtocol.ContextControlProto contextControlProto) { 427 synchronized (this.evaluatorDescriptor) { 428 LOG.log(Level.FINEST, "Context control message to {0}", this.evaluatorId); 429 this.contextControlHandler.send(contextControlProto); 430 } 431 } 432 433 /** 434 * Forward the EvaluatorControlProto to the EvaluatorRuntime 435 * 436 * @param evaluatorControlProto message contains evaluator control information. 437 */ 438 void sendEvaluatorControlMessage(final EvaluatorRuntimeProtocol.EvaluatorControlProto evaluatorControlProto) { 439 synchronized (this.evaluatorDescriptor) { 440 this.evaluatorControlHandler.send(evaluatorControlProto); 441 } 442 } 443 444 /** 445 * Handle task status messages. 446 * 447 * @param taskStatusProto message contains the current task status. 448 */ 449 private void onTaskStatusMessage(final ReefServiceProtos.TaskStatusProto taskStatusProto) { 450 451 if (!(this.task.isPresent() && this.task.get().getId().equals(taskStatusProto.getTaskId()))) { 452 if (taskStatusProto.getState() == ReefServiceProtos.State.INIT || 453 taskStatusProto.getState() == ReefServiceProtos.State.FAILED || 454 taskStatusProto.getRecovery() // for task from recovered evaluators 455 ) { 456 457 // FAILED is a legal first state of a Task as it could have failed during construction. 458 this.task = Optional.of( 459 new TaskRepresenter(taskStatusProto.getTaskId(), 460 this.contextRepresenters.getContext(taskStatusProto.getContextId()), 461 this.messageDispatcher, 462 this, 463 this.exceptionCodec)); 464 } else { 465 throw new RuntimeException("Received an message of state " + taskStatusProto.getState() + 466 ", not INIT or FAILED for Task " + taskStatusProto.getTaskId() + " which we haven't heard from before."); 467 } 468 } 469 this.task.get().onTaskStatusMessage(taskStatusProto); 470 471 if (this.task.get().isNotRunning()) { 472 LOG.log(Level.FINEST, "Task no longer running. De-registering it."); 473 this.task = Optional.empty(); 474 } 475 } 476 477 /** 478 * Resource status information from the (actual) resource manager. 479 */ 480 public void onResourceStatusMessage(final ResourceStatusEvent resourceStatusEvent) { 481 synchronized (this.evaluatorDescriptor) { 482 LOG.log(Level.FINEST, "Resource manager state update: {0}", resourceStatusEvent.getState()); 483 if (this.stateManager.isDoneOrFailedOrKilled()) { 484 LOG.log(Level.FINE, "Ignoring resource status update for Evaluator {0} which is already in state {1}.", 485 new Object[]{this.getId(), this.stateManager}); 486 } else if (isDoneOrFailedOrKilled(resourceStatusEvent) && this.stateManager.isAllocatedOrSubmittedOrRunning()) { 487 // something is wrong. The resource manager reports that the Evaluator is done or failed, but the Driver assumes 488 // it to be alive. 489 final StringBuilder messageBuilder = new StringBuilder("Evaluator [") 490 .append(this.evaluatorId) 491 .append("] is assumed to be in state [") 492 .append(this.stateManager.toString()) 493 .append("]. But the resource manager reports it to be in state [") 494 .append(resourceStatusEvent.getState()) 495 .append("]."); 496 497 if (this.stateManager.isSubmitted()) { 498 messageBuilder 499 .append(" This most likely means that the Evaluator suffered a failure before establishing a communications link to the driver."); 500 } else if (this.stateManager.isAllocated()) { 501 messageBuilder.append(" This most likely means that the Evaluator suffered a failure before being used."); 502 } else if (this.stateManager.isRunning()) { 503 messageBuilder.append(" This means that the Evaluator failed but wasn't able to send an error message back to the driver."); 504 } 505 if (this.task.isPresent()) { 506 messageBuilder.append(" Task [") 507 .append(this.task.get().getId()) 508 .append("] was running when the Evaluator crashed."); 509 } 510 this.isResourceReleased = true; 511 512 if (resourceStatusEvent.getState() == ReefServiceProtos.State.KILLED) { 513 this.onEvaluatorException(new EvaluatorKilledByResourceManagerException(this.evaluatorId, messageBuilder.toString())); 514 } else { 515 this.onEvaluatorException(new EvaluatorException(this.evaluatorId, messageBuilder.toString())); 516 } 517 } 518 } 519 } 520 521 @Override 522 public String toString() { 523 return "EvaluatorManager:" 524 + " id=" + this.evaluatorId 525 + " state=" + this.stateManager 526 + " task=" + this.task; 527 } 528 529 // Dynamic Parameters 530 @NamedParameter(doc = "The Evaluator Identifier.") 531 public final static class EvaluatorIdentifier implements Name<String> { 532 } 533 534 @NamedParameter(doc = "The Evaluator Host.") 535 public final static class EvaluatorDescriptorName implements Name<EvaluatorDescriptorImpl> { 536 } 537}