Java tutorial
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.hadoop.yarn.client.api; import java.io.IOException; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.function.Supplier; import java.util.List; import com.google.common.annotations.VisibleForTesting; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience.Private; import org.apache.hadoop.classification.InterfaceAudience.Public; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.service.AbstractService; import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse; import org.apache.hadoop.yarn.api.protocolrecords.RegisterApplicationMasterResponse; import org.apache.hadoop.yarn.api.records.Container; import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.ContainerUpdateType; import org.apache.hadoop.yarn.api.records.ExecutionType; import org.apache.hadoop.yarn.api.records.ExecutionTypeRequest; import org.apache.hadoop.yarn.api.records.FinalApplicationStatus; import org.apache.hadoop.yarn.api.records.Priority; import org.apache.hadoop.yarn.api.records.Resource; import org.apache.hadoop.yarn.api.records.SchedulingRequest; import org.apache.hadoop.yarn.api.records.UpdateContainerRequest; import org.apache.hadoop.yarn.api.resource.PlacementConstraint; import org.apache.hadoop.yarn.client.api.impl.AMRMClientImpl; import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.hadoop.yarn.util.resource.Resources; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @InterfaceAudience.Public @InterfaceStability.Stable public abstract class AMRMClient<T extends AMRMClient.ContainerRequest> extends AbstractService { private static final Logger LOG = LoggerFactory.getLogger(AMRMClient.class); private TimelineV2Client timelineV2Client; /** * Create a new instance of AMRMClient. * For usage: * <pre> * {@code * AMRMClient.<T>createAMRMClientContainerRequest() * }</pre> * @return the newly create AMRMClient instance. */ @Public public static <T extends ContainerRequest> AMRMClient<T> createAMRMClient() { AMRMClient<T> client = new AMRMClientImpl<T>(); return client; } private NMTokenCache nmTokenCache; @Private protected AMRMClient(String name) { super(name); nmTokenCache = NMTokenCache.getSingleton(); } /** * Object to represent a single container request for resources. Scheduler * documentation should be consulted for the specifics of how the parameters * are honored. * * By default, YARN schedulers try to allocate containers at the requested * locations but they may relax the constraints in order to expedite meeting * allocations limits. They first relax the constraint to the same rack as the * requested node and then to anywhere in the cluster. The relaxLocality flag * may be used to disable locality relaxation and request containers at only * specific locations. The following conditions apply. * <ul> * <li>Within a priority, all container requests must have the same value for * locality relaxation. Either enabled or disabled.</li> * <li>If locality relaxation is disabled, then across requests, locations at * different network levels may not be specified. E.g. its invalid to make a * request for a specific node and another request for a specific rack.</li> * <li>If locality relaxation is disabled, then only within the same request, * a node and its rack may be specified together. This allows for a specific * rack with a preference for a specific node within that rack.</li> * <li></li> * </ul> * To re-enable locality relaxation at a given priority, all pending requests * with locality relaxation disabled must be first removed. Then they can be * added back with locality relaxation enabled. * * All getters return immutable values. */ public static class ContainerRequest { private Resource capability; private List<String> nodes; private List<String> racks; private Priority priority; private long allocationRequestId; private boolean relaxLocality = true; private String nodeLabelsExpression; private ExecutionTypeRequest executionTypeRequest = ExecutionTypeRequest.newInstance(); private String resourceProfile = null; /** * Instantiates a {@link ContainerRequest} with the given constraints and * locality relaxation enabled. * * @param capability * The {@link Resource} to be requested for each container. * @param nodes * Any hosts to request that the containers are placed on. * @param racks * Any racks to request that the containers are placed on. The * racks corresponding to any hosts requested will be automatically * added to this list. * @param priority * The priority at which to request the containers. Higher * priorities have lower numerical values. */ public ContainerRequest(Resource capability, String[] nodes, String[] racks, Priority priority) { this(capability, nodes, racks, priority, true, null); } @VisibleForTesting public ContainerRequest(Resource capability, String[] nodes, String[] racks, Priority priority, String profile) { this(capability, nodes, racks, priority, 0, true, null, ExecutionTypeRequest.newInstance(), profile); } /** * Instantiates a {@link ContainerRequest} with the given constraints and * locality relaxation enabled. * * @param capability * The {@link Resource} to be requested for each container. * @param nodes * Any hosts to request that the containers are placed on. * @param racks * Any racks to request that the containers are placed on. The * racks corresponding to any hosts requested will be automatically * added to this list. * @param priority * The priority at which to request the containers. Higher * priorities have lower numerical values. * @param allocationRequestId Allocation Request Id */ @Public @InterfaceStability.Evolving public ContainerRequest(Resource capability, String[] nodes, String[] racks, Priority priority, long allocationRequestId) { this(capability, nodes, racks, priority, allocationRequestId, true, null, ExecutionTypeRequest.newInstance()); } /** * Instantiates a {@link ContainerRequest} with the given constraints. * * @param capability * The {@link Resource} to be requested for each container. * @param nodes * Any hosts to request that the containers are placed on. * @param racks * Any racks to request that the containers are placed on. The * racks corresponding to any hosts requested will be automatically * added to this list. * @param priority * The priority at which to request the containers. Higher * priorities have lower numerical values. * @param relaxLocality * If true, containers for this request may be assigned on hosts * and racks other than the ones explicitly requested. */ public ContainerRequest(Resource capability, String[] nodes, String[] racks, Priority priority, boolean relaxLocality) { this(capability, nodes, racks, priority, relaxLocality, null); } /** * Instantiates a {@link ContainerRequest} with the given constraints. * * @param capability * The {@link Resource} to be requested for each container. * @param nodes * Any hosts to request that the containers are placed on. * @param racks * Any racks to request that the containers are placed on. The * racks corresponding to any hosts requested will be automatically * added to this list. * @param priority * The priority at which to request the containers. Higher * priorities have lower numerical values. * @param relaxLocality * If true, containers for this request may be assigned on hosts * and racks other than the ones explicitly requested. * @param allocationRequestId Allocation Request Id */ @Public @InterfaceStability.Evolving public ContainerRequest(Resource capability, String[] nodes, String[] racks, Priority priority, long allocationRequestId, boolean relaxLocality) { this(capability, nodes, racks, priority, allocationRequestId, relaxLocality, null, ExecutionTypeRequest.newInstance()); } /** * Instantiates a {@link ContainerRequest} with the given constraints. * * @param capability * The {@link Resource} to be requested for each container. * @param nodes * Any hosts to request that the containers are placed on. * @param racks * Any racks to request that the containers are placed on. The * racks corresponding to any hosts requested will be automatically * added to this list. * @param priority * The priority at which to request the containers. Higher * priorities have lower numerical values. * @param relaxLocality * If true, containers for this request may be assigned on hosts * and racks other than the ones explicitly requested. * @param nodeLabelsExpression * Set node labels to allocate resource, now we only support * asking for only a single node label */ public ContainerRequest(Resource capability, String[] nodes, String[] racks, Priority priority, boolean relaxLocality, String nodeLabelsExpression) { this(capability, nodes, racks, priority, 0, relaxLocality, nodeLabelsExpression, ExecutionTypeRequest.newInstance()); } /** * Instantiates a {@link ContainerRequest} with the given constraints. * * @param capability * The {@link Resource} to be requested for each container. * @param nodes * Any hosts to request that the containers are placed on. * @param racks * Any racks to request that the containers are placed on. The * racks corresponding to any hosts requested will be automatically * added to this list. * @param priority * The priority at which to request the containers. Higher * priorities have lower numerical values. * @param allocationRequestId * The allocationRequestId of the request. To be used as a tracking * id to match Containers allocated against this request. Will * default to 0 if not specified. * @param relaxLocality * If true, containers for this request may be assigned on hosts * and racks other than the ones explicitly requested. * @param nodeLabelsExpression * Set node labels to allocate resource, now we only support * asking for only a single node label */ @Public @InterfaceStability.Evolving public ContainerRequest(Resource capability, String[] nodes, String[] racks, Priority priority, long allocationRequestId, boolean relaxLocality, String nodeLabelsExpression) { this(capability, nodes, racks, priority, allocationRequestId, relaxLocality, nodeLabelsExpression, ExecutionTypeRequest.newInstance()); } /** * Instantiates a {@link ContainerRequest} with the given constraints. * * @param capability * The {@link Resource} to be requested for each container. * @param nodes * Any hosts to request that the containers are placed on. * @param racks * Any racks to request that the containers are placed on. The * racks corresponding to any hosts requested will be automatically * added to this list. * @param priority * The priority at which to request the containers. Higher * priorities have lower numerical values. * @param allocationRequestId * The allocationRequestId of the request. To be used as a tracking * id to match Containers allocated against this request. Will * default to 0 if not specified. * @param relaxLocality * If true, containers for this request may be assigned on hosts * and racks other than the ones explicitly requested. * @param nodeLabelsExpression * Set node labels to allocate resource, now we only support * asking for only a single node label * @param executionTypeRequest * Set the execution type of the container request. */ public ContainerRequest(Resource capability, String[] nodes, String[] racks, Priority priority, long allocationRequestId, boolean relaxLocality, String nodeLabelsExpression, ExecutionTypeRequest executionTypeRequest) { this(capability, nodes, racks, priority, allocationRequestId, relaxLocality, nodeLabelsExpression, executionTypeRequest, null); } /** * Instantiates a {@link ContainerRequest} with the given constraints. * * @param capability * The {@link Resource} to be requested for each container. * @param nodes * Any hosts to request that the containers are placed on. * @param racks * Any racks to request that the containers are placed on. The * racks corresponding to any hosts requested will be automatically * added to this list. * @param priority * The priority at which to request the containers. Higher * priorities have lower numerical values. * @param allocationRequestId * The allocationRequestId of the request. To be used as a tracking * id to match Containers allocated against this request. Will * default to 0 if not specified. * @param relaxLocality * If true, containers for this request may be assigned on hosts * and racks other than the ones explicitly requested. * @param nodeLabelsExpression * Set node labels to allocate resource, now we only support * asking for only a single node label * @param executionTypeRequest * Set the execution type of the container request. * @param profile * Set the resource profile for the container request */ public ContainerRequest(Resource capability, String[] nodes, String[] racks, Priority priority, long allocationRequestId, boolean relaxLocality, String nodeLabelsExpression, ExecutionTypeRequest executionTypeRequest, String profile) { this.allocationRequestId = allocationRequestId; this.capability = capability; this.nodes = (nodes != null ? ImmutableList.copyOf(nodes) : null); this.racks = (racks != null ? ImmutableList.copyOf(racks) : null); this.priority = priority; this.relaxLocality = relaxLocality; this.nodeLabelsExpression = nodeLabelsExpression; this.executionTypeRequest = executionTypeRequest; this.resourceProfile = profile; sanityCheck(); } // Validate request private void sanityCheck() { Preconditions.checkArgument(capability != null, "The Resource to be requested for each container " + "should not be null "); Preconditions.checkArgument(priority != null, "The priority at which to request containers should not be null "); Preconditions.checkArgument( !(!relaxLocality && (racks == null || racks.size() == 0) && (nodes == null || nodes.size() == 0)), "Can't turn off locality relaxation on a " + "request with no location constraints"); } private ContainerRequest() { }; public Resource getCapability() { return capability; } public List<String> getNodes() { return nodes; } public List<String> getRacks() { return racks; } public Priority getPriority() { return priority; } public long getAllocationRequestId() { return allocationRequestId; } public boolean getRelaxLocality() { return relaxLocality; } public String getNodeLabelExpression() { return nodeLabelsExpression; } public ExecutionTypeRequest getExecutionTypeRequest() { return executionTypeRequest; } public String getResourceProfile() { return resourceProfile; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Capability[").append(capability).append("]"); sb.append("Priority[").append(priority).append("]"); sb.append("AllocationRequestId[").append(allocationRequestId).append("]"); sb.append("ExecutionTypeRequest[").append(executionTypeRequest).append("]"); sb.append("Resource Profile[").append(resourceProfile).append("]"); return sb.toString(); } public static ContainerRequestBuilder newBuilder() { return new ContainerRequestBuilder(); } /** * Class to construct instances of {@link ContainerRequest} with specific * options. */ public static final class ContainerRequestBuilder { private ContainerRequest containerRequest = new ContainerRequest(); public ContainerRequestBuilder capability(Resource capability) { containerRequest.capability = capability; return this; } public ContainerRequestBuilder nodes(String[] nodes) { containerRequest.nodes = (nodes != null ? ImmutableList.copyOf(nodes) : null); return this; } public ContainerRequestBuilder racks(String[] racks) { containerRequest.racks = (racks != null ? ImmutableList.copyOf(racks) : null); return this; } public ContainerRequestBuilder priority(Priority priority) { containerRequest.priority = priority; return this; } public ContainerRequestBuilder allocationRequestId(long allocationRequestId) { containerRequest.allocationRequestId = allocationRequestId; return this; } public ContainerRequestBuilder relaxLocality(boolean relaxLocality) { containerRequest.relaxLocality = relaxLocality; return this; } public ContainerRequestBuilder nodeLabelsExpression(String nodeLabelsExpression) { containerRequest.nodeLabelsExpression = nodeLabelsExpression; return this; } public ContainerRequestBuilder executionTypeRequest(ExecutionTypeRequest executionTypeRequest) { containerRequest.executionTypeRequest = executionTypeRequest; return this; } public ContainerRequestBuilder resourceProfile(String resourceProfile) { containerRequest.resourceProfile = resourceProfile; return this; } public ContainerRequest build() { containerRequest.sanityCheck(); return containerRequest; } } } /** * Add a Collection of SchedulingRequests. The AMRMClient will ensure that * all requests in the same batch are sent in the same allocate call. * @param schedulingRequests Collection of Scheduling Requests. */ @Public @InterfaceStability.Unstable public void addSchedulingRequests(Collection<SchedulingRequest> schedulingRequests) { } /** * Register the application master. This must be called before any * other interaction * @param appHostName Name of the host on which master is running * @param appHostPort Port master is listening on * @param appTrackingUrl URL at which the master info can be seen * @return <code>RegisterApplicationMasterResponse</code> * @throws YarnException * @throws IOException */ public abstract RegisterApplicationMasterResponse registerApplicationMaster(String appHostName, int appHostPort, String appTrackingUrl) throws YarnException, IOException; /** * Register the application master. This must be called before any * other interaction * @param appHostName Name of the host on which master is running * @param appHostPort Port master is listening on * @param appTrackingUrl URL at which the master info can be seen * @param placementConstraints Placement Constraints mappings. * @return <code>RegisterApplicationMasterResponse</code> * @throws YarnException * @throws IOException */ @Public @InterfaceStability.Unstable public RegisterApplicationMasterResponse registerApplicationMaster(String appHostName, int appHostPort, String appTrackingUrl, Map<Set<String>, PlacementConstraint> placementConstraints) throws YarnException, IOException { throw new YarnException("Not supported"); } /** * Request additional containers and receive new container allocations. * Requests made via <code>addContainerRequest</code> are sent to the * <code>ResourceManager</code>. New containers assigned to the master are * retrieved. Status of completed containers and node health updates are also * retrieved. This also doubles up as a heartbeat to the ResourceManager and * must be made periodically. The call may not always return any new * allocations of containers. App should not make concurrent allocate * requests. May cause request loss. * * <p> * Note : If the user has not removed container requests that have already * been satisfied, then the re-register may end up sending the entire * container requests to the RM (including matched requests). Which would mean * the RM could end up giving it a lot of new allocated containers. * </p> * * @param progressIndicator Indicates progress made by the master * @return the response of the allocate request * @throws YarnException * @throws IOException */ public abstract AllocateResponse allocate(float progressIndicator) throws YarnException, IOException; /** * Unregister the application master. This must be called in the end. * @param appStatus Success/Failure status of the master * @param appMessage Diagnostics message on failure * @param appTrackingUrl New URL to get master info * @throws YarnException * @throws IOException */ public abstract void unregisterApplicationMaster(FinalApplicationStatus appStatus, String appMessage, String appTrackingUrl) throws YarnException, IOException; /** * Request containers for resources before calling <code>allocate</code> * @param req Resource request */ public abstract void addContainerRequest(T req); /** * Remove previous container request. The previous container request may have * already been sent to the ResourceManager. So even after the remove request * the app must be prepared to receive an allocation for the previous request * even after the remove request * @param req Resource request */ public abstract void removeContainerRequest(T req); /** * Request container resource change before calling <code>allocate</code>. * Any previous pending resource change request of the same container will be * removed. * * Application that calls this method is expected to maintain the * <code>Container</code>s that are returned from previous successful * allocations or resource changes. By passing in the existing container and a * target resource capability to this method, the application requests the * ResourceManager to change the existing resource allocation to the target * resource allocation. * * @deprecated use * {@link #requestContainerUpdate(Container, UpdateContainerRequest)} * * @param container The container returned from the last successful resource * allocation or resource change * @param capability The target resource capability of the container */ @Deprecated public void requestContainerResourceChange(Container container, Resource capability) { Preconditions.checkNotNull(container, "Container cannot be null!!"); Preconditions.checkNotNull(capability, "UpdateContainerRequest cannot be null!!"); requestContainerUpdate(container, UpdateContainerRequest.newInstance(container.getVersion(), container.getId(), Resources.fitsIn(capability, container.getResource()) ? ContainerUpdateType.DECREASE_RESOURCE : ContainerUpdateType.INCREASE_RESOURCE, capability, null)); } /** * Request a container update before calling <code>allocate</code>. * Any previous pending update request of the same container will be * removed. * * @param container The container returned from the last successful resource * allocation or update * @param updateContainerRequest The <code>UpdateContainerRequest</code>. */ public abstract void requestContainerUpdate(Container container, UpdateContainerRequest updateContainerRequest); /** * Release containers assigned by the Resource Manager. If the app cannot use * the container or wants to give up the container then it can release them. * The app needs to make new requests for the released resource capability if * it still needs it. eg. it released non-local resources * @param containerId */ public abstract void releaseAssignedContainer(ContainerId containerId); /** * Get the currently available resources in the cluster. * A valid value is available after a call to allocate has been made * @return Currently available resources */ public abstract Resource getAvailableResources(); /** * Get the current number of nodes in the cluster. * A valid values is available after a call to allocate has been made * @return Current number of nodes in the cluster */ public abstract int getClusterNodeCount(); /** * Get outstanding <code>ContainerRequest</code>s matching the given * parameters. These ContainerRequests should have been added via * <code>addContainerRequest</code> earlier in the lifecycle. For performance, * the AMRMClient may return its internal collection directly without creating * a copy. Users should not perform mutable operations on the return value. * Each collection in the list contains requests with identical * <code>Resource</code> size that fit in the given capability. In a * collection, requests will be returned in the same order as they were added. * * NOTE: This API only matches Container requests that were created by the * client WITHOUT the allocationRequestId being set. * * @return Collection of request matching the parameters */ @InterfaceStability.Evolving public abstract List<? extends Collection<T>> getMatchingRequests(Priority priority, String resourceName, Resource capability); /** * Get outstanding <code>ContainerRequest</code>s matching the given * parameters. These ContainerRequests should have been added via * <code>addContainerRequest</code> earlier in the lifecycle. For performance, * the AMRMClient may return its internal collection directly without creating * a copy. Users should not perform mutable operations on the return value. * Each collection in the list contains requests with identical * <code>Resource</code> size that fit in the given capability. In a * collection, requests will be returned in the same order as they were added. * specify an <code>ExecutionType</code>. * * NOTE: This API only matches Container requests that were created by the * client WITHOUT the allocationRequestId being set. * * @param priority Priority * @param resourceName Location * @param executionType ExecutionType * @param capability Capability * @return Collection of request matching the parameters */ @InterfaceStability.Evolving public List<? extends Collection<T>> getMatchingRequests(Priority priority, String resourceName, ExecutionType executionType, Resource capability) { throw new UnsupportedOperationException( "The sub-class extending" + " AMRMClient is expected to implement this !!"); } @InterfaceStability.Evolving public List<? extends Collection<T>> getMatchingRequests(Priority priority, String resourceName, ExecutionType executionType, Resource capability, String profile) { throw new UnsupportedOperationException( "The sub-class extending" + " AMRMClient is expected to implement this !!"); } /** * Get outstanding <code>ContainerRequest</code>s matching the given * allocationRequestId. These ContainerRequests should have been added via * <code>addContainerRequest</code> earlier in the lifecycle. For performance, * the AMRMClient may return its internal collection directly without creating * a copy. Users should not perform mutable operations on the return value. * * NOTE: This API only matches Container requests that were created by the * client WITH the allocationRequestId being set to a non-default value. * * @param allocationRequestId Allocation Request Id * @return Collection of request matching the parameters */ @InterfaceStability.Evolving public abstract Collection<T> getMatchingRequests(long allocationRequestId); /** * Update application's blacklist with addition or removal resources. * * @param blacklistAdditions list of resources which should be added to the * application blacklist * @param blacklistRemovals list of resources which should be removed from the * application blacklist */ public abstract void updateBlacklist(List<String> blacklistAdditions, List<String> blacklistRemovals); /** * Set the NM token cache for the <code>AMRMClient</code>. This cache must * be shared with the {@link NMClient} used to manage containers for the * <code>AMRMClient</code> * <p> * If a NM token cache is not set, the {@link NMTokenCache#getSingleton()} * singleton instance will be used. * * @param nmTokenCache the NM token cache to use. */ public void setNMTokenCache(NMTokenCache nmTokenCache) { this.nmTokenCache = nmTokenCache; } /** * Get the NM token cache of the <code>AMRMClient</code>. This cache must be * shared with the {@link NMClient} used to manage containers for the * <code>AMRMClient</code>. * <p> * If a NM token cache is not set, the {@link NMTokenCache#getSingleton()} * singleton instance will be used. * * @return the NM token cache. */ public NMTokenCache getNMTokenCache() { return nmTokenCache; } /** * Register TimelineV2Client to AMRMClient. Writer's address for the timeline * V2 client will be updated dynamically if registered. * * @param client the timeline v2 client to register */ public void registerTimelineV2Client(TimelineV2Client client) { timelineV2Client = client; } /** * Get registered timeline v2 client. * @return the registered timeline v2 client */ public TimelineV2Client getRegisteredTimelineV2Client() { return this.timelineV2Client; } /** * Update application's tracking url on next heartbeat. * * @param trackingUrl new tracking url for this application */ @Public @InterfaceStability.Unstable public void updateTrackingUrl(String trackingUrl) { // Unimplemented. } /** * Wait for <code>check</code> to return true for each 1000 ms. * See also {@link #waitFor(java.util.function.Supplier, int)} * and {@link #waitFor(java.util.function.Supplier, int, int)} * @param check the condition for which it should wait */ public void waitFor(Supplier<Boolean> check) throws InterruptedException { waitFor(check, 1000); } /** * Wait for <code>check</code> to return true for each * <code>checkEveryMillis</code> ms. * See also {@link #waitFor(java.util.function.Supplier, int, int)} * @param check user defined checker * @param checkEveryMillis interval to call <code>check</code> */ public void waitFor(Supplier<Boolean> check, int checkEveryMillis) throws InterruptedException { waitFor(check, checkEveryMillis, 1); } /** * Wait for <code>check</code> to return true for each * <code>checkEveryMillis</code> ms. In the main loop, this method will log * the message "waiting in main loop" for each <code>logInterval</code> times * iteration to confirm the thread is alive. * @param check user defined checker * @param checkEveryMillis interval to call <code>check</code> * @param logInterval interval to log for each */ public void waitFor(Supplier<Boolean> check, int checkEveryMillis, int logInterval) throws InterruptedException { Preconditions.checkNotNull(check, "check should not be null"); Preconditions.checkArgument(checkEveryMillis >= 0, "checkEveryMillis should be positive value"); Preconditions.checkArgument(logInterval >= 0, "logInterval should be positive value"); int loggingCounter = logInterval; do { if (LOG.isDebugEnabled()) { LOG.debug("Check the condition for main loop."); } boolean result = check.get(); if (result) { LOG.info("Exits the main loop."); return; } if (--loggingCounter <= 0) { LOG.info("Waiting in main loop."); loggingCounter = logInterval; } Thread.sleep(checkEveryMillis); } while (true); } }