org.cloudifysource.utilitydomain.admin.TimedAdmin.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudifysource.utilitydomain.admin.TimedAdmin.java

Source

/*******************************************************************************
 * Copyright (c) 2013 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.utilitydomain.admin;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import net.jini.core.discovery.LookupLocator;

import org.apache.commons.lang.StringUtils;
import org.openspaces.admin.Admin;
import org.openspaces.admin.AdminFactory;
import org.openspaces.admin.esm.ElasticServiceManager;
import org.openspaces.admin.pu.ProcessingUnit;
import org.openspaces.admin.space.Space;
import org.openspaces.security.AdminFilter;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * Wraps the {@link Admin} object in order to monitor the object usage and close it after it is no longer in use.
 * This is intended to minimize memory and network utilization by unused {@link Admin} objects.
 * 
 * @author noak
 * @since 2.7.1
 */
public class TimedAdmin {

    private static Logger logger = Logger.getLogger(TimedAdmin.class.getName());
    // TODO noak: make configurable
    private static final long MAX_IDLE_TIME_MILLIS = 120 * 1000; // defaults to 120 seconds
    private static final long POLLING_INTERVAL_MILLIS = 10 * 1000; // defaults to 10 seconds

    private long lastUsed = System.currentTimeMillis();
    private Admin admin;

    private boolean discoverUnmanagedSpaces;
    boolean running;
    private int statisticsHistorySize = Admin.DEFAULT_HISTORY_SIZE;
    private String groups;
    private String locators;
    private Class[] discoveryServices;
    private AdminFilter adminFilter;
    private ExecutorService executor;

    public void setDiscoveryServices(final Class[] discoveryServices) {
        this.discoveryServices = discoveryServices;
    }

    public void setStatisticsHistorySize(int statisticsHistorySize) {
        this.statisticsHistorySize = statisticsHistorySize;
    }

    public String[] getAdminGroups() {
        if (admin != null) {
            return admin.getGroups();
        }
        return null;
    }

    public void setGroups(final String groups) {
        this.groups = groups;
    }

    public LookupLocator[] getAdminLocators() {
        if (admin != null) {
            return admin.getLocators();
        }
        return null;
    }

    public void setLocators(final String locators) {
        this.locators = locators;
    }

    public void setAdminFilter(final AdminFilter adminFilter) {
        this.adminFilter = adminFilter;
    }

    public void discoverUnmanagedSpaces() {
        this.discoverUnmanagedSpaces = true;
    }

    /***********
     * Creates an admin instance if required.
     * A timing thread is also created to monitor the admin expity time and terminate it if needed. 
     */
    private synchronized void initAdmin() {
        logger.finest("getting admin object");
        if (admin == null) {
            createAdmin();
        } else {
            logger.finest("Using a cached Admin object");
        }
        updateTimestamp();
    }

    private void createAdmin() {
        logger.finest("Creating a new Admin object...");

        final AdminFactory factory = new AdminFactory();
        factory.useDaemonThreads(true);

        if (StringUtils.isNotBlank(groups)) {
            factory.addGroups(groups);
        }

        if (StringUtils.isNotBlank(locators)) {
            factory.addLocators(locators);
        }

        if (adminFilter != null) {
            factory.adminFilter(adminFilter);
        }

        if (discoveryServices != null) {
            factory.setDiscoveryServices(discoveryServices);
        }

        if (discoverUnmanagedSpaces) {
            factory.discoverUnmanagedSpaces();
        }

        this.admin = factory.createAdmin();
        this.admin.setStatisticsHistorySize(statisticsHistorySize);

        logger.finest("Created new Admin Object with groups: " + Arrays.toString(this.admin.getGroups())
                + " and Locators: " + Arrays.toString(this.admin.getLocators()));

        updateTimestamp();
        startTimingThread();
    }

    /**
     * Creates and starts a thread that monitors the admin object usage - if the object was not used for longer than 
     * the maximum idle time, the object is closed and nullified.
     */
    private synchronized void startTimingThread() {

        // create daemon threads, so the timing thread won't keep the process alive
        executor = Executors.newSingleThreadExecutor(new ThreadFactory() {

            @Override
            public Thread newThread(Runnable runnable) {
                Thread thread = Executors.defaultThreadFactory().newThread(runnable);
                thread.setDaemon(true);
                thread.setName("AdminTimingThread");
                return thread;
            }
        });

        executor.execute(new Runnable() {
            @Override
            public void run() {
                running = true;
                while (running) {
                    try {
                        if (admin != null && (lastUsed + MAX_IDLE_TIME_MILLIS < System.currentTimeMillis())) {
                            logger.fine("Closing expired admin object");
                            admin.close();
                            admin = null;
                            running = false;
                        }
                        Thread.sleep(POLLING_INTERVAL_MILLIS);
                    } catch (final InterruptedException e) {
                        // ignore
                    }
                }
            }
        });

        executor.shutdown();
    }

    /**
     * Waits until a space by the specified name is found, or the timeout is reached.
     * @param spaceName The name of the requested space
     * @param timeout The timeout length
     * @param timeunit The timeout length time unit
     * @return The space, if found in the given time frame; null otherwise
     */
    public Space waitForSpace(final String spaceName, final long timeout, final TimeUnit timeunit) {
        validateTimeout(timeout, timeunit, "waiting for space instance");
        initAdmin();
        return admin.getSpaces().waitFor(spaceName, timeout, timeunit);
    }

    /**
     * Returns a space based on its name.
     * @param spaceName The name of the requested space
     * @return The space if found; null otherwise
     */
    public Space getSpaceByName(final String spaceName) {
        initAdmin();
        return admin.getSpaces().getSpaceByName(spaceName);
    }

    /**
     * Waits until a processing unit by the specified name is found, or the timeout is reached.
     * @param puName The name of the requested space
     * @param timeout The timeout length
     * @param timeunit The timeout length time unit
     * @return The processing unit, if found in the given time frame; null otherwise
     */
    public ProcessingUnit waitForPU(final String puName, final long timeout, final TimeUnit timeunit) {
        validateTimeout(timeout, timeunit, "waiting for PU");
        initAdmin();
        return admin.getProcessingUnits().waitFor(puName, timeout, timeunit);
    }

    /**
     * Waits until at least the provided number of Processing Unit Instances are found, or the timeout is reached.
     * @param pu The processing unit object
     * @param numberOfProcessingUnitInstances The required number of instances
     * @param timeout the timeout length
     * @param timeunit The timeout length time unit
     * @return true if the numbers of PUIs found is equal or more than required, false otherwise
     */
    public boolean waitForPUI(final ProcessingUnit pu, int numberOfPUInstances, long timeout, TimeUnit timeunit) {
        validateTimeout(timeout, timeunit, "waiting for PU instance");
        initAdmin();
        return pu.waitFor(numberOfPUInstances, timeout, timeunit);
    }

    /**
     * Waits until all lookup services are found, or the timeout is reached.
     * @param numberOfLookupServices The number of requested lookup services
     * @param timeout The timeout length
     * @param timeunit The timeout length time unit
     * @return True if all lookup services were found; false otherwise
     */
    public boolean waitForLookupServices(int numberOfLookupServices, long timeout, TimeUnit timeunit) {
        validateTimeout(timeout, timeunit, "waiting for lookup service");
        initAdmin();
        return admin.getLookupServices().waitFor(numberOfLookupServices, timeout, timeunit);
    }

    /**
     * Waits until an {@link ElasticServiceManager} is found, or the timeout is reached.
     * @return The ElasticServiceManager if found in the given time frame; null otherwise
     */
    public ElasticServiceManager waitForElasticServiceManager() {
        initAdmin();
        return admin.getElasticServiceManagers().waitForAtLeastOne();
    }

    /**
     * Closes the admin object and stops the timing thread.
     */
    public void close() {
        logger.finest("Closing the admin object and stopping the timing thread");
        if (admin != null) {
            admin.close();
            admin = null;
        }

        // this should cause the timing thread to end
        running = false;
    }

    /**
     * Returns the state of the underlying admin object: if it's set - return true, otherwise return false.
     * @return If the admin is set (not null) - return true, otherwise return false
     */
    public boolean isAdminObjectAlive() {
        if (admin == null) {
            return false;
        } else {
            return true;
        }
    }

    private synchronized void updateTimestamp() {
        lastUsed = System.currentTimeMillis();
    }

    /**
     * Validates the given action timeout is not shorter than the admin timeout, and issues a warning if it is.
     */
    private void validateTimeout(final long timeout, final TimeUnit timeunit, final String actionDescription) {
        if (timeunit.toMillis(timeout) >= MAX_IDLE_TIME_MILLIS) {
            logger.warning("Admin object might expire prematurely! The specified timeout for " + actionDescription
                    + " was set to " + timeout + " " + timeunit.toString() + " while the admin timeout is "
                    + MAX_IDLE_TIME_MILLIS / 1000 + " seconds");
        }
    }

}