Package org.cloudfoundry.ide.eclipse.server.core.internal.client

Source Code of org.cloudfoundry.ide.eclipse.server.core.internal.client.BaseClientRequest

/*******************************************************************************
* Copyright (c) 2014 Pivotal Software, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of 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.
*  Contributors:
*     Pivotal Software, Inc. - initial API and implementation
********************************************************************************/
package org.cloudfoundry.ide.eclipse.server.core.internal.client;

import org.cloudfoundry.client.lib.CloudFoundryOperations;
import org.cloudfoundry.ide.eclipse.server.core.internal.CloudErrorUtil;
import org.cloudfoundry.ide.eclipse.server.core.internal.Messages;
import org.cloudfoundry.ide.eclipse.server.core.internal.log.HttpTracer;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.osgi.util.NLS;

/**
* Executes Cloud Foundry client ( {@link CloudFoundryOperations} ) operations,
* and optionally handles reattempts IFF an error is thrown during the
* operation.
* <p/>
* By default, operations are performed only once and any error thrown will not
* result in further attempts. Subclasses can override this behaviour.
*/
public abstract class BaseClientRequest<T> {

  /**
   *
   */
  private final String label;

  public BaseClientRequest(String label) {
    Assert.isNotNull(label);
    this.label = label;
  }

  /**
   *
   * @return result of client operation
   * @throws CoreException if failure occurred while attempting to execute the
   * client operation.
   */
  public T run(IProgressMonitor monitor) throws CoreException {

    SubMonitor subProgress = SubMonitor.convert(monitor, label, 100);

    CloudFoundryOperations client = getClient(subProgress);
    if (client == null) {
      throw CloudErrorUtil.toCoreException(NLS.bind(Messages.ERROR_NO_CLIENT, label));
    }

    HttpTracer.getCurrent().trace(client);
    try {
      return runAndWait(client, subProgress);
    }
    catch (CoreException ce) {
      // See if it is a connection error. If so, parse it into readable
      // form.
      String connectionError = CloudErrorUtil.getConnectionError(ce);
      if (connectionError != null) {
        throw CloudErrorUtil.asCoreException(connectionError, ce, true);
      }
      else {
        throw ce;
      }
    }
    finally {
      subProgress.done();
    }

  }

  /**
   * Performs a client operation, and if necessary, re-attempts the operation
   * after a certain interval IFF an error occurs based on
   * {@link #getTotalTimeWait()} and
   * {@link #getWaitInterval(Throwable, SubMonitor)}.
   * <p/>
   * The default behaviour is to only attempt a client operation once and quit
   * after an error is encountered. Subclasses may modify this behaviour by
   * overriding {@link #getTotalTimeWait()} and
   * {@link #getWaitInterval(Throwable, SubMonitor)}
   * <p/>
   * Note that reattempts are only decided based on errors thrown by the
   * client invocation, not by results generated by the client invocation.
   * @param client client whose operations are invoked. Never null.
   * @param subProgress
   * @return result of operation. Can be null.
   * @throws CoreException if fatal error occurred while performing the
   * operation (i.e. error that causes the operation to no longer be
   * reattempted)
   * @throws OperationCanceledException if further attempts are cancelled even
   * if time still remains for additional attempts.
   */
  protected T runAndWait(CloudFoundryOperations client, SubMonitor subProgress) throws CoreException,
      OperationCanceledException {
    Throwable error = null;

    boolean reattempt = true;
    long timeLeft = getTotalTimeWait();

    // Either this operation returns a result during the waiting period or
    // an error occurred, and error
    // gets thrown

    while (reattempt) {

      long interval = -1;

      try {
        return doRun(client, subProgress);
      }
      catch (Throwable e) {
        error = e;
      }

      interval = getWaitInterval(error, subProgress);
      timeLeft -= interval;
      reattempt = !subProgress.isCanceled() && timeLeft >= 0 && interval > 0;
      if (reattempt) {

        try {
          Thread.sleep(interval);
        }
        catch (InterruptedException e) {
          // Ignore, continue with the next iteration
        }
      }
    }

    if (subProgress.isCanceled()) {
      throw new OperationCanceledException();
    }
    else if (error instanceof CoreException) {
      throw (CoreException) error;
    }
    else {
      throw CloudErrorUtil.toCoreException(error);
    }
  }

  /**
   * Given an error, determine how long the operation should wait before
   * trying again before timeout is reached. In order for attempt to be tried
   * again, value must be positive. Any value less than or equal to 0 will not
   * result in further attempts.
   *
   * <p/>
   *
   * By default it returns -1, meaning that the request is attempted only
   * once, and any exceptions thrown will not result in reattempts. Subclasses
   * can override to determine different reattempt conditions.
   * @param exception to determine how long to wait until another attempt is
   * made to run the operation. Note that if total timeout time is shorter
   * than the interval, no further attempts will be made.
   * @param monitor
   * @return interval value greater than 0 if attempt is to be made . Any
   * other value equal or less than 0 will result in the operation terminating
   * without further reattempts.
   * @throw CoreException if failed to determine interval. A CoreException
   * will result in no further attempts.
   */
  protected long getWaitInterval(Throwable exception, SubMonitor monitor) throws CoreException {
    return -1;
  }

  /**
   * Perform the actual client operation. The client is guaranteed to be
   * non-null at this stage.
   * @param client non-null client
   * @param progress
   * @return result of operation.
   * @throws CoreException
   */
  protected abstract T doRun(CloudFoundryOperations client, SubMonitor progress) throws CoreException;

  /**
   * This must never be null. This is the client used to perform operations.
   * @return Non-null Java client.
   * @throws CoreException if failed to obtain a client
   */
  protected abstract CloudFoundryOperations getClient(IProgressMonitor monitor) throws CoreException;

  /**
   * Total amount of time to wait. If less than the wait interval length, only
   * one attempt will be made {@link #getWaitInterval(Throwable, SubMonitor)}
   * @return
   */
  protected long getTotalTimeWait() {
    return CloudOperationsConstants.DEFAULT_CF_CLIENT_REQUEST_TIMEOUT;
  }

}
TOP

Related Classes of org.cloudfoundry.ide.eclipse.server.core.internal.client.BaseClientRequest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.