/*
* ProActive Parallel Suite(TM):
* The Open Source library for parallel and distributed
* Workflows & Scheduling, Orchestration, Cloud Automation
* and Big Data Analysis on Enterprise Grids & Clouds.
*
* Copyright (c) 2007 - 2017 ActiveEon
* Contact: contact@activeeon.com
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation: version 3 of
* the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If needed, contact us to obtain a release under GPL Version 2 or 3
* or a different license than the AGPL.
*/
package org.ow2.proactive.resourcemanager.nodesource.dataspace;
import java.io.File;
import java.io.Serializable;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.Selectors;
import org.apache.commons.vfs2.impl.DefaultFileSystemManager;
import org.apache.log4j.Logger;
import org.objectweb.proactive.api.PAActiveObject;
import org.objectweb.proactive.core.node.Node;
import org.objectweb.proactive.core.util.wrapper.BooleanWrapper;
import org.objectweb.proactive.extensions.dataspaces.core.BaseScratchSpaceConfiguration;
import org.objectweb.proactive.extensions.dataspaces.core.DataSpacesNodes;
import org.objectweb.proactive.extensions.dataspaces.core.InputOutputSpaceConfiguration;
import org.objectweb.proactive.extensions.dataspaces.vfs.VFSFactory;
import org.objectweb.proactive.extensions.vfsprovider.FileSystemServerDeployer;
/**
* DataSpaceNodeConfigurationAgent is used to configure and close DataSpaces knowledge
*
* @author The ProActive Team
* @since ProActive Scheduling 1.1
*/
public class DataSpaceNodeConfigurationAgent implements Serializable {
private static final transient Logger logger = Logger.getLogger(DataSpaceNodeConfigurationAgent.class);
/**
* This property is used by scheduling when configuring node to define the location of the scratch dir and must be renamed carefully.
* It is also defined in TaskLauncher.
*/
public static final String NODE_DATASPACE_SCRATCHDIR = "node.dataspace.scratchdir";
/**
* This property is used by scheduling when configuring node to define the location of the cache dir. If the property is not defined,
* the scratch location will be used to create the cache dir
*/
public static final String NODE_DATASPACE_CACHEDIR = "node.dataspace.cachedir";
/**
* This property contains the invalidation period for a file, in ms. If set with a value greater than 0, each file older than the specified period will be deleted.
*/
public static final String NODE_DATASPACE_CACHE_INVALIDATION_PERIOD = "node.dataspace.cache.invalidation.period";
/**
* This property contains the interval period used to start the cache cleaning process.
*/
public static final String NODE_DATASPACE_CACHE_CLEANING_PERIOD = "node.dataspace.cache.cleaning.period";
/**
* Name of the CacheSpace for DataSpaces registration
*/
public static final String CACHESPACE_NAME = "CACHESPACE";
/**
* Default subfolder name for the cache
**/
public static final String DEFAULT_CACHE_SUBFOLDER_NAME = "cache";
/**
* Default cache cleaning period (interval at which the cache is cleaned. Default to one hour
**/
public static final long DEFAULT_CACHE_CLEANING_PERIOD = 3600000L;
/**
* Default cache invalidation period (files older than this date will be deleted). Default to two weeks
**/
public static final long DEFAULT_CACHE_INVALIDATION_PERIOD = 1210000000L;
/**
* file system server controlling the cache
*/
private static transient FileSystemServerDeployer cacheServer;
/**
* Configuration of the cache server (used by the TaskLauncher to register the dataspace to the naming service
*/
private static transient InputOutputSpaceConfiguration cacheSpaceConfiguration;
/**
* VFS direct interface to the cache, used by the cleaning mechanism to delete files
*/
private static transient DefaultFileSystemManager fileSystemManager;
/**
* URL of the cache root folder
*/
private static String rootCacheUri;
/**
* Timer used by the cleaning mechanism
*/
private static transient Timer cleaningTimer;
/**
* Lock used by the cleaning mechanism to ensure that the cleaning is not run concurrently with a Task
*/
private static transient ReentrantReadWriteLock cacheCleaningRWLock = new ReentrantReadWriteLock();
/**
* Create a new instance of DataSpaceNodeConfigurationAgent
* Used by ProActive
*/
public DataSpaceNodeConfigurationAgent() {
}
public boolean configureNode() {
try {
Node node = PAActiveObject.getActiveObjectNode(PAActiveObject.getStubOnThis());
logger.info("Start of dataspace configuration for node " + node);
// configure node for Data Spaces
String baseScratchDir = getBaseScratchDir();
final BaseScratchSpaceConfiguration scratchConf = new BaseScratchSpaceConfiguration((String) null,
baseScratchDir);
DataSpacesNodes.configureNode(node, scratchConf);
logger.info("Dataspace configuration for node " + node + " successful.");
} catch (Throwable t) {
logger.error("Cannot configure dataSpace", t);
return false;
}
startCacheSpace();
return true;
}
private String getBaseScratchDir() {
String scratchDir;
if (System.getProperty(NODE_DATASPACE_SCRATCHDIR) == null) {
//if scratch dir java property is not set, set to default
scratchDir = System.getProperty("java.io.tmpdir");
} else {
//else use the property
scratchDir = System.getProperty(NODE_DATASPACE_SCRATCHDIR);
}
return scratchDir;
}
private String getCacheDir() {
String cacheDir;
if (System.getProperty(NODE_DATASPACE_CACHEDIR) == null) {
//if scratch dir java property is not set, set to default
cacheDir = (new File(getBaseScratchDir(), DEFAULT_CACHE_SUBFOLDER_NAME)).getAbsolutePath();
} else {
// else use the property
cacheDir = System.getProperty(NODE_DATASPACE_CACHEDIR);
}
return cacheDir;
}
private static long getCacheCleaningPeriod() {
long cacheCleaningPeriod = DEFAULT_CACHE_CLEANING_PERIOD;
if (System.getProperty(NODE_DATASPACE_CACHE_CLEANING_PERIOD) != null) {
cacheCleaningPeriod = Long.parseLong(System.getProperty(NODE_DATASPACE_CACHE_CLEANING_PERIOD));
}
return cacheCleaningPeriod;
}
private static long getCacheInvalidationPeriod() {
long cacheInvalidationPeriod = DEFAULT_CACHE_INVALIDATION_PERIOD;
if (System.getProperty(NODE_DATASPACE_CACHE_INVALIDATION_PERIOD) != null) {
cacheInvalidationPeriod = Long.parseLong(System.getProperty(NODE_DATASPACE_CACHE_INVALIDATION_PERIOD));
}
return cacheInvalidationPeriod;
}
/**
* Returns the configuration of the server cache
*
* @return
*/
public static InputOutputSpaceConfiguration getCacheSpaceConfiguration() {
return cacheSpaceConfiguration;
}
/**
* Starts the Cache Space server
*
* @return
*/
public boolean startCacheSpace() {
if (cacheSpaceConfiguration == null) {
try {
String cacheLocation = getCacheDir();
cacheServer = new FileSystemServerDeployer(CACHESPACE_NAME, cacheLocation, true, true);
logger.info("Cache server started at " + Arrays.toString(cacheServer.getVFSRootURLs()));
String hostname = InetAddress.getLocalHost().getHostName();
cacheSpaceConfiguration = InputOutputSpaceConfiguration.createOutputSpaceConfiguration(Arrays.asList(cacheServer.getVFSRootURLs()),
cacheLocation,
hostname,
CACHESPACE_NAME);
fileSystemManager = VFSFactory.createDefaultFileSystemManager();
rootCacheUri = new File(cacheLocation).toURI().toURL().toExternalForm();
if (getCacheInvalidationPeriod() > 0) {
startCacheCleaningDaemon();
}
} catch (Exception e) {
logger.error("Error occurred when starting the cache server", e);
return false;
}
}
return true;
}
/**
* This method must be used by the TaskLauncher to prevent the cleaning timer to run concurrently to a Task
* As a read lock is used, multiple readers can acquire the lock at the same time
*
* @throws InterruptedException
*/
public static void lockCacheSpaceCleaning() throws InterruptedException {
cacheCleaningRWLock.readLock().lockInterruptibly();
}
/**
* This method must be used by the TaskLauncher at the end of a Task execution, allowing the cleaning timer to run
* <p>
* The cleaning timer will be released only when all read locks have been released.
*
* @throws InterruptedException
*/
public static void unlockCacheSpaceCleaning() {
cacheCleaningRWLock.readLock().unlock();
}
private void startCacheCleaningDaemon() {
cleaningTimer = new Timer("CacheCleaningTimer", true);
long cleaningInterval = getCacheCleaningPeriod();
cleaningTimer.schedule(new CleanTimerTask(), cleaningInterval, cleaningInterval);
}
public BooleanWrapper closeNodeConfiguration() {
try {
cacheServer.terminate();
cleaningTimer.cancel();
DataSpacesNodes.closeNodeConfig(PAActiveObject.getActiveObjectNode(PAActiveObject.getStubOnThis()));
} catch (Throwable t) {
logger.error("Cannot close dataSpace configuration !", t);
throw new RuntimeException(t);
}
PAActiveObject.terminateActiveObject(false);
return new BooleanWrapper(true);
}
/**
* This class contains the logic of the cleaning mechanism
*/
private static class CleanTimerTask extends TimerTask {
@Override
public void run() {
try {
long invalidationPeriod = getCacheInvalidationPeriod();
long currentTime = System.currentTimeMillis();
// lock the timer in write mode, this will prevent any Task to start during the cleaning process
if (cacheCleaningRWLock.writeLock().tryLock()) {
try {
FileObject rootFO = fileSystemManager.resolveFile(rootCacheUri);
FileObject[] files = rootFO.findFiles(Selectors.EXCLUDE_SELF);
for (FileObject file : files) {
if (currentTime - file.getContent().getLastModifiedTime() > invalidationPeriod) {
logger.info("[Cache Space cleaner] deleting " + file);
file.delete();
}
}
} finally {
cacheCleaningRWLock.writeLock().unlock();
}
}
} catch (Exception e) {
logger.error("Error when cleaning files in cache", e);
}
}
}
}