/*******************************************************************************
* Copyright (c) 2011 GigaSpaces Technologies Ltd. All rights reserved
*
* Licensed 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.cloudifysource.shell.rest;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.cloudifysource.dsl.internal.CloudifyConstants;
import org.cloudifysource.restclient.ErrorStatusException;
import org.cloudifysource.restclient.GSRestClient;
import org.cloudifysource.shell.ConditionLatch;
import org.cloudifysource.shell.ConditionLatch.Predicate;
import org.cloudifysource.shell.exceptions.CLIException;
import org.cloudifysource.shell.installer.CLIEventsDisplayer;
/**
* The RestLifecycleEventsLatch will poll the rest for installation lifecycle events
* and print the new events to the CLI console.
* The polling latch will stop polling the rest for three reasons:
* * The timeout period expired.
* * Installation on the remote rest gateway ended.
* * An exception was thrown in the polling thread on remote server
*
* @author adaml
*
*/
public class RestLifecycleEventsLatch {
private static final Logger logger = Logger.getLogger(RestLifecycleEventsLatch.class.getName());
private static final long MIN_POLLING_INTERVAL = 2000;
private static final String DEFAULT_TIMEOUT_MESSAGE = "installation timed out";
private long pollingInterval = MIN_POLLING_INTERVAL;
private String timeoutMessage = DEFAULT_TIMEOUT_MESSAGE;
private final CLIEventsDisplayer displayer;
private String pollingID;
private GSRestClient client;
private int cursor = 0;
private boolean isDone = false;
private String url;
private Map<String, Object> lifecycleEventLogs = null;
private long remoteTaskLeaseExpiration;
/**
* Constructor.
*/
public RestLifecycleEventsLatch() {
this.displayer = new CLIEventsDisplayer();
}
/**
* Waits for lifecycle events. This method will poll the rest for installation lifecycle events
* and print the new events to the CLI console.
*
* @param timeout units to wait.
* @param timeUnit unit type.
* @throws InterruptedException .
* @throws TimeoutException .
* @throws CLIException .
*/
public void waitForLifecycleEvents(final int timeout, final TimeUnit timeUnit)
throws InterruptedException, TimeoutException, CLIException {
createConditionLatch(timeout, TimeUnit.MINUTES).waitFor(new Predicate() {
@SuppressWarnings("unchecked")
@Override
public boolean isDone() throws CLIException, InterruptedException {
url = "/service/lifecycleEventContainerID/" + pollingID
+ "/cursor/" + cursor;
try {
lifecycleEventLogs = (Map<String, Object>) client.get(url);
} catch (final ErrorStatusException e) {
if (e.getCause() instanceof IOException) {
displayer.printEvent("Communication Error accessing " + url);
return false;
}
throw new CLIException("Operation failed. Reason: " + e.getMessage(), e);
}
List<String> events = (List<String>) lifecycleEventLogs.get(CloudifyConstants.LIFECYCLE_LOGS);
cursor = (Integer) lifecycleEventLogs.get(CloudifyConstants.CURSOR_POS);
isDone = (Boolean) lifecycleEventLogs.get(CloudifyConstants.IS_TASK_DONE);
remoteTaskLeaseExpiration = Long.valueOf((String) lifecycleEventLogs.
get(CloudifyConstants.SERVER_POLLING_TASK_EXPIRATION_MILLI)) + System.currentTimeMillis();
if (System.currentTimeMillis() > remoteTaskLeaseExpiration) {
throw new CLIException("Events polling task has expired on remote server side");
}
if (events == null) {
displayer.printNoChange();
} else {
displayer.printEvents(events);
}
if (isDone) {
displayer.eraseCurrentLine();
return true;
}
return false;
}
});
}
/**
* Continue an already started polling task. Used for when polling was interrupted on the client side.
*
* @param timeout .
* @param timeUnit .
* @throws InterruptedException .
* @throws TimeoutException .
* @throws CLIException if the polling task has expired on the remote server side
*/
public void continueWaitForLifecycleEvents(final int timeout, final TimeUnit timeUnit)
throws InterruptedException, TimeoutException, CLIException {
if (System.currentTimeMillis() > this.remoteTaskLeaseExpiration) {
throw new CLIException("Events polling task has expired on remote server side");
}
waitForLifecycleEvents(timeout, timeUnit);
}
/**
* Creates a condition latch object with the specified timeout. If the condition times out, a
* {@link TimeoutException} is thrown.
*
* @param timeout
* number of timeunits to wait
* @param timeunit
* type of timeunits to wait
* @return Configured condition latch
*/
private ConditionLatch createConditionLatch(final long timeout, final TimeUnit timeunit) {
return new ConditionLatch().timeout(timeout, timeunit)
.pollingInterval(this.pollingInterval, TimeUnit.MILLISECONDS)
.timeoutErrorMessage(this.timeoutMessage);
}
/**
* Sets the polling interval.
*
* @param pollingInterval
* Polling interval.
* @param timeUnit
* The polling interval time unit.
*/
public void setPollingInterval(final long pollingInterval, final TimeUnit timeUnit) {
long pollingIntervalInMillis = timeUnit.toMillis(pollingInterval);
if (!(pollingIntervalInMillis < MIN_POLLING_INTERVAL)) {
this.pollingInterval = pollingIntervalInMillis;
} else {
logger.log(Level.INFO,
"Polling interveal was set to the minimum polling"
+ " interval allowed: " + MIN_POLLING_INTERVAL + "seconds");
}
}
public void setPollingId(final String pollingID) {
this.pollingID = pollingID;
}
public void setRestClient(final GSRestClient client) {
this.client = client;
}
/**
* Sets the timeout exception message.
* @param timeoutMessage The timeout exception message.
*/
public void setTimeoutMessage(final String timeoutMessage) {
this.timeoutMessage = timeoutMessage;
}
}