org.openspaces.grid.gsm.machines.plugins.NonBlockingElasticMachineProvisioningAdapter.java Source code

Java tutorial

Introduction

Here is the source code for org.openspaces.grid.gsm.machines.plugins.NonBlockingElasticMachineProvisioningAdapter.java

Source

/*******************************************************************************
 * 
 * Copyright (c) 2012 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.openspaces.grid.gsm.machines.plugins;

import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openspaces.admin.gsa.GSAReservationId;
import org.openspaces.admin.gsa.GridServiceAgent;
import org.openspaces.admin.gsa.events.ElasticGridServiceAgentProvisioningProgressChangedEventListener;
import org.openspaces.admin.machine.events.ElasticMachineProvisioningProgressChangedEventListener;
import org.openspaces.admin.pu.elastic.ElasticMachineProvisioningConfig;
import org.openspaces.admin.zone.config.ExactZonesConfig;
import org.openspaces.grid.gsm.capacity.CapacityRequirement;
import org.openspaces.grid.gsm.capacity.CapacityRequirements;
import org.openspaces.grid.gsm.capacity.NumberOfMachinesCapacityRequirement;
import org.openspaces.grid.gsm.machines.FailedGridServiceAgent;
import org.openspaces.grid.gsm.machines.FutureCleanupCloudResources;
import org.openspaces.grid.gsm.machines.FutureGridServiceAgent;
import org.openspaces.grid.gsm.machines.FutureGridServiceAgents;
import org.openspaces.grid.gsm.machines.FutureStoppedMachine;
import org.openspaces.grid.gsm.machines.StartedGridServiceAgent;
import org.openspaces.grid.gsm.machines.exceptions.NoClassDefFoundElasticMachineProvisioningException;
import org.openspaces.grid.gsm.machines.isolation.ElasticProcessingUnitMachineIsolation;
import org.openspaces.grid.gsm.machines.plugins.exceptions.ElasticGridServiceAgentProvisioningException;
import org.openspaces.grid.gsm.machines.plugins.exceptions.ElasticMachineProvisioningException;

/**
 * An adapter that wraps an {@link ElasticMachineProvisioning} and exposes a {@link NonBlockingElasticMachineProvisioning}
 *
 * @see NonBlockingElasticMachineProvisioning
 * @see ElasticMachineProvisioning
 * 
 * @author itaif
 */
public class NonBlockingElasticMachineProvisioningAdapter implements NonBlockingElasticMachineProvisioning {

    private ElasticMachineProvisioning machineProvisioning;

    private final ExecutorService executorService;

    private final ScheduledThreadPoolExecutor scheduledExecutorService;

    private static final Log logger = LogFactory.getLog(NonBlockingElasticMachineProvisioningAdapter.class);

    private static final int THROTTLING_DELAY_SECONDS = 0;

    public NonBlockingElasticMachineProvisioningAdapter(ElasticMachineProvisioning machineProvisioning,
            ExecutorService executorService, ScheduledThreadPoolExecutor scheduledExecutorService) {
        this.machineProvisioning = machineProvisioning;
        this.executorService = executorService;
        this.scheduledExecutorService = scheduledExecutorService;
    }

    @Override
    public FutureGridServiceAgent[] startMachinesAsync(final CapacityRequirements capacityRequirements,
            final ExactZonesConfig zones, final FailedGridServiceAgent[] failedAgents, final long duration,
            final TimeUnit unit) {

        if (!isStartMachineSupported()) {
            throw new UnsupportedOperationException();
        }

        final GSAReservationId reservationId = GSAReservationId.randomGSAReservationId();
        final CapacityRequirements singleMachineCapacity = machineProvisioning.getCapacityOfSingleMachine();
        int numberOfMachines = calcNumberOfMachines(capacityRequirements, machineProvisioning);
        if (numberOfMachines < failedAgents.length) {
            throw new IllegalArgumentException("capacity requirements should be at least " + failedAgents.length
                    + " machines for failure recovery. " + "Instead found " + numberOfMachines
                    + " machines, capacity = " + capacityRequirements);
        }
        FutureGridServiceAgent[] futureAgents = new FutureGridServiceAgent[numberOfMachines];

        for (int i = 0; i < futureAgents.length; i++) {
            final AtomicReference<Object> ref = new AtomicReference<Object>(null);

            final int throttlingDelay = i * THROTTLING_DELAY_SECONDS;
            final long start = System.currentTimeMillis();
            final long end = start + throttlingDelay * 1000 + unit.toMillis(duration);
            final FailedGridServiceAgent failedAgent = failedAgents.length > i ? failedAgents[i] : null;
            submit(new Runnable() {
                public void run() {
                    try {
                        logger.info("Starting a new machine");
                        StartedGridServiceAgent agent = machineProvisioning.startMachine(zones, reservationId,
                                failedAgent, duration, unit);
                        ref.set(agent);
                        logger.info("New machine started");
                    } catch (ElasticMachineProvisioningException e) {
                        ref.set(e);
                    } catch (ElasticGridServiceAgentProvisioningException e) {
                        ref.set(e);
                    } catch (InterruptedException e) {
                        ref.set(e);
                    } catch (TimeoutException e) {
                        ref.set(e);
                    } catch (NoClassDefFoundError e) {
                        ref.set((new NoClassDefFoundElasticMachineProvisioningException(e)));
                    } catch (Throwable e) {
                        logger.error("Unexpected exception:" + e.getMessage(), e);
                        ref.set(e);
                    }
                }

            }, throttlingDelay, TimeUnit.SECONDS);

            futureAgents[i] = new FutureGridServiceAgent() {

                public boolean isDone() {
                    return System.currentTimeMillis() > end || ref.get() != null;
                }

                public ExecutionException getException() {
                    Object result = ref.get();
                    if (result != null && result instanceof Throwable) {
                        Throwable throwable = (Throwable) result;
                        return new ExecutionException(throwable.getMessage(), throwable);
                    }
                    return null;
                }

                public boolean isTimedOut() {
                    Object result = ref.get();
                    return System.currentTimeMillis() > end
                            || (result != null && result instanceof TimeoutException);
                }

                public Date getTimestamp() {
                    return new Date(start);
                }

                public StartedGridServiceAgent get()
                        throws ExecutionException, IllegalStateException, TimeoutException {

                    Object result = ref.get();

                    if (result == null) {
                        if (System.currentTimeMillis() > end) {
                            throw new TimeoutException("Starting a new machine took more than "
                                    + unit.toSeconds(duration) + " seconds to complete.");
                        }

                        throw new IllegalStateException("Async operation is not done yet.");
                    }

                    if (getException() != null) {
                        throw getException();
                    }

                    return (StartedGridServiceAgent) result;
                }

                public NonBlockingElasticMachineProvisioning getMachineProvisioning() {
                    return NonBlockingElasticMachineProvisioningAdapter.this;
                }

                public CapacityRequirements getFutureCapacity() {
                    return singleMachineCapacity;
                }

                public GSAReservationId getReservationId() {
                    return reservationId;
                }

                public FailedGridServiceAgent getFailedGridServiceAgent() {
                    return failedAgent;
                }
            };
        }
        return futureAgents;
    }

    @Override
    public FutureStoppedMachine stopMachineAsync(final GridServiceAgent agent, final long duration,
            final TimeUnit unit) {

        final AtomicReference<Throwable> atomicExceptionRef = new AtomicReference<Throwable>();
        final AtomicBoolean atomicDone = new AtomicBoolean(false);

        if (!isStartMachineSupported()) {
            throw new UnsupportedOperationException();
        }

        final long start = System.currentTimeMillis();
        final long end = System.currentTimeMillis() + unit.toMillis(duration);
        final String hostAddress = agent.getMachine().getHostAddress();
        submit(new Runnable() {

            @Override
            public void run() {

                logger.info("Stopping machine " + hostAddress);
                try {
                    NonBlockingElasticMachineProvisioningAdapter.this.machineProvisioning
                            .stopMachine(new StartedGridServiceAgent(agent, null), duration, unit);
                    logger.info("machine " + hostAddress + " successfully stopped.");
                    atomicDone.set(true);
                } catch (ElasticMachineProvisioningException e) {
                    atomicExceptionRef.set(e);
                } catch (ElasticGridServiceAgentProvisioningException e) {
                    atomicExceptionRef.set(e);
                } catch (InterruptedException e) {
                    atomicExceptionRef.set(e);
                } catch (TimeoutException e) {
                    atomicExceptionRef.set(e);
                } catch (NoClassDefFoundError e) {
                    atomicExceptionRef.set((new NoClassDefFoundElasticMachineProvisioningException(e)));
                } catch (Throwable e) {
                    atomicExceptionRef.set(e);
                }
            }

        });

        return new FutureStoppedMachine() {

            @Override
            public boolean isTimedOut() {
                Throwable exception = atomicExceptionRef.get();
                return (exception instanceof TimeoutException) || (!isDone() && System.currentTimeMillis() > end);
            }

            @Override
            public boolean isDone() {
                return atomicDone.get() || (atomicExceptionRef.get() != null);
            }

            @Override
            public Date getTimestamp() {
                return new Date(start);
            }

            @Override
            public ExecutionException getException() {
                ExecutionException executionException = null;
                Throwable throwable = atomicExceptionRef.get();
                if (throwable != null) {
                    executionException = new ExecutionException(throwable.getMessage(), throwable);
                }
                return executionException;
            }

            @Override
            public Void get() throws ExecutionException, IllegalStateException, TimeoutException {

                if (!isDone()) {
                    // dont allow calling get() before isDone returns true.
                    throw new IllegalStateException("Async operation has not completed yet");
                }

                if (isDone() && getException() == null) {
                    // all is ok
                    return null;
                } else {
                    throw getException();
                }
            }

            @Override
            public GridServiceAgent getGridServiceAgent() {
                return agent;
            }
        };

    }

    private void submit(Runnable runnable) {
        executorService.submit(runnable);
    }

    private void submit(final Runnable runnable, long delay, TimeUnit unit) {
        scheduledExecutorService.schedule(new Runnable() {
            public void run() {
                submit(runnable);
            }
        }, delay, unit);
    }

    @Override
    public FutureGridServiceAgents getDiscoveredMachinesAsync(final long duration, final TimeUnit unit) {

        final AtomicReference<Object> ref = new AtomicReference<Object>(null);
        final long start = System.currentTimeMillis();
        final long end = start + unit.toMillis(duration);

        submit(new Runnable() {
            public void run() {
                try {
                    GridServiceAgent[] agents = machineProvisioning.getDiscoveredMachines(duration, unit);
                    ref.set(agents);
                } catch (ElasticMachineProvisioningException e) {
                    ref.set(e);
                } catch (ElasticGridServiceAgentProvisioningException e) {
                    ref.set(e);
                } catch (InterruptedException e) {
                    ref.set(e);
                } catch (TimeoutException e) {
                    ref.set(e);
                } catch (NoClassDefFoundError e) {
                    ref.set((new NoClassDefFoundElasticMachineProvisioningException(e)));
                } catch (Throwable e) {
                    logger.error("Unexpected exception", e);
                    ref.set(e);
                }
            }

        });

        return new FutureGridServiceAgents() {

            public boolean isDone() {
                return System.currentTimeMillis() > end || ref.get() != null;
            }

            public ExecutionException getException() {
                Object result = ref.get();
                if (result != null && result instanceof Throwable) {
                    Throwable throwable = (Throwable) result;
                    return new ExecutionException(throwable.getMessage(), throwable);
                }
                return null;
            }

            public boolean isTimedOut() {
                Object result = ref.get();
                return System.currentTimeMillis() > end || (result != null && result instanceof TimeoutException);
            }

            public Date getTimestamp() {
                return new Date(start);
            }

            public GridServiceAgent[] get() throws ExecutionException, IllegalStateException, TimeoutException {

                Object result = ref.get();

                if (result == null) {
                    if (System.currentTimeMillis() > end) {
                        throw new TimeoutException("Starting a new machine took more than "
                                + unit.toSeconds(duration) + " seconds to complete.");
                    }

                    throw new IllegalStateException("Async operation is not done yet.");
                }

                if (getException() != null) {
                    throw getException();
                }

                return (GridServiceAgent[]) result;

            }
        };
    }

    @Override
    public ElasticMachineProvisioningConfig getConfig() {
        return machineProvisioning.getConfig();
    }

    @Override
    public boolean isStartMachineSupported() {
        return machineProvisioning.isStartMachineSupported();
    }

    private static int calcNumberOfMachines(CapacityRequirements capacityRequirements,
            ElasticMachineProvisioning machineProvisioning) {

        NumberOfMachinesCapacityRequirement numberOfMachinesCapacityRequirement = capacityRequirements
                .getRequirement(new NumberOfMachinesCapacityRequirement().getType());

        int maxNumberOfMachines = Math.max(1, numberOfMachinesCapacityRequirement.getNumberOfMachines());

        CapacityRequirements singleMachineCapacityRequirements = machineProvisioning.getCapacityOfSingleMachine()
                .subtractOrZero(machineProvisioning.getConfig().getReservedCapacityPerMachine());

        for (CapacityRequirement capacityRequirement : capacityRequirements.getRequirements()) {
            CapacityRequirement singleMachinecapacityRequirement = singleMachineCapacityRequirements
                    .getRequirement(capacityRequirement.getType());
            if (!singleMachinecapacityRequirement.equalsZero()) {
                int numberOfMachines = (int) Math
                        .ceil(capacityRequirement.divide(singleMachinecapacityRequirement));
                if (numberOfMachines > maxNumberOfMachines) {
                    maxNumberOfMachines = numberOfMachines;
                }
            }
        }

        logger.info(maxNumberOfMachines + " " + "machines are required in order to satisfy capacity requirements: "
                + capacityRequirements);

        return maxNumberOfMachines;
    }

    @Override
    public FutureCleanupCloudResources cleanupCloudResources(final long duration, final TimeUnit unit) {
        final AtomicReference<Throwable> atomicExceptionRef = new AtomicReference<Throwable>();
        final AtomicBoolean atomicDone = new AtomicBoolean(false);

        if (!isStartMachineSupported()) {
            throw new UnsupportedOperationException();
        }

        final long start = System.currentTimeMillis();
        final long end = System.currentTimeMillis() + unit.toMillis(duration);
        submit(new Runnable() {

            @Override
            public void run() {

                logger.info("Cleaning cloud resources");
                try {
                    NonBlockingElasticMachineProvisioningAdapter.this.machineProvisioning
                            .cleanupMachineResources(duration, unit);
                    logger.info("Cleaned cloud resources.");
                    atomicDone.set(true);
                } catch (ElasticMachineProvisioningException e) {
                    atomicExceptionRef.set(e);
                } catch (InterruptedException e) {
                    atomicExceptionRef.set(e);
                } catch (TimeoutException e) {
                    atomicExceptionRef.set(e);
                } catch (NoClassDefFoundError e) {
                    atomicExceptionRef.set((new NoClassDefFoundElasticMachineProvisioningException(e)));
                } catch (Throwable e) {
                    atomicExceptionRef.set(e);
                }
            }

        });

        return new FutureCleanupCloudResources() {

            boolean mark;

            @Override
            public boolean isTimedOut() {
                Throwable exception = atomicExceptionRef.get();
                return (exception instanceof TimeoutException) || (!isDone() && System.currentTimeMillis() > end);
            }

            @Override
            public boolean isDone() {
                return atomicDone.get() || (atomicExceptionRef.get() != null);
            }

            @Override
            public Date getTimestamp() {
                return new Date(start);
            }

            @Override
            public ExecutionException getException() {
                ExecutionException executionException = null;
                Throwable throwable = atomicExceptionRef.get();
                if (throwable != null) {
                    executionException = new ExecutionException(throwable.getMessage(), throwable);
                }
                return executionException;
            }

            @Override
            public Void get() throws ExecutionException, IllegalStateException, TimeoutException {

                if (!isDone()) {
                    // dont allow calling get() before isDone returns true.
                    throw new IllegalStateException("Async operation has not completed yet");
                }

                if (isDone() && getException() == null) {
                    // all is ok
                    return null;
                } else {
                    throw getException();
                }
            }

            @Override
            public boolean isMarked() {
                return mark;
            }

            @Override
            public void mark() {
                mark = true;
            }
        };

    }

    @Override
    public void setElasticProcessingUnitMachineIsolation(ElasticProcessingUnitMachineIsolation isolation) {
        machineProvisioning.setElasticProcessingUnitMachineIsolation(isolation);
    }

    @Override
    public void setElasticMachineProvisioningProgressChangedEventListener(
            ElasticMachineProvisioningProgressChangedEventListener machineEventListener) {
        machineProvisioning.setElasticMachineProvisioningProgressChangedEventListener(machineEventListener);
    }

    @Override
    public void setElasticGridServiceAgentProvisioningProgressEventListener(
            ElasticGridServiceAgentProvisioningProgressChangedEventListener agentEventListener) {
        machineProvisioning.setElasticGridServiceAgentProvisioningProgressEventListener(agentEventListener);
    }

    public ElasticMachineProvisioning getElasticMachineProvisioning() {
        return machineProvisioning;
    }
}