Java tutorial
/* * ################################################################ * * ProActive Parallel Suite(TM): The Java(TM) library for * Parallel, Distributed, Multi-Core Computing for * Enterprise Grids & Clouds * * Copyright (C) 1997-2012 INRIA/University of * Nice-Sophia Antipolis/ActiveEon * Contact: proactive@ow2.org or 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 library 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. * * Initial developer(s): The ProActive Team * http://proactive.inria.fr/team_members.htm * Contributor(s): * * ################################################################ * $$PROACTIVE_INITIAL_DEV$$ */ package org.objectweb.proactive.extensions.dataspaces.vfs; import java.io.File; import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; import org.apache.commons.vfs.Capability; import org.apache.commons.vfs.FileObject; import org.apache.commons.vfs.FileSystem; import org.apache.commons.vfs.Selectors; import org.apache.commons.vfs.impl.DefaultFileSystemManager; import org.apache.log4j.Logger; import org.objectweb.proactive.Body; import org.objectweb.proactive.core.ProActiveRuntimeException; import org.objectweb.proactive.core.node.Node; import org.objectweb.proactive.core.util.log.Loggers; import org.objectweb.proactive.core.util.log.ProActiveLogger; import org.objectweb.proactive.extensions.dataspaces.Utils; import org.objectweb.proactive.extensions.dataspaces.core.ApplicationScratchSpace; import org.objectweb.proactive.extensions.dataspaces.core.BaseScratchSpaceConfiguration; import org.objectweb.proactive.extensions.dataspaces.core.DataSpacesURI; import org.objectweb.proactive.extensions.dataspaces.core.NodeScratchSpace; import org.objectweb.proactive.extensions.dataspaces.core.ScratchSpaceConfiguration; import org.objectweb.proactive.extensions.dataspaces.core.SpaceInstanceInfo; import org.objectweb.proactive.extensions.dataspaces.exceptions.ConfigurationException; import org.objectweb.proactive.extensions.dataspaces.exceptions.FileSystemException; /** * Implementation of {@link NodeScratchSpace} using Apache Commons VFS library. */ public class VFSNodeScratchSpaceImpl implements NodeScratchSpace { private static final Logger logger = ProActiveLogger.getLogger(Loggers.DATASPACES_CONFIGURATOR); private BaseScratchSpaceConfiguration baseScratchConfiguration; private Node node; private boolean configured; private FileObject partialSpaceFile; private DefaultFileSystemManager fileSystemManager; /** * Inner class to implement {@link ApplicationScratchSpace} interface. */ private class AppScratchSpaceImpl implements ApplicationScratchSpace { private final FileObject spaceFile; private final Map<String, DataSpacesURI> scratches = new HashMap<String, DataSpacesURI>(); private final SpaceInstanceInfo spaceInstanceInfo; private AppScratchSpaceImpl(final long appId) throws FileSystemException { logger.debug("Initializing application node scratch space"); final String appIdString = Long.toString(appId); final String runtimeId = Utils.getRuntimeId(node); final String nodeId = Utils.getNodeId(node); try { this.spaceFile = createEmptyDirectoryRelative(partialSpaceFile, appIdString); spaceFile.close(); } catch (org.apache.commons.vfs.FileSystemException x) { logger.error("Could not create directory for application scratch space", x); throw new FileSystemException(x); } try { final ScratchSpaceConfiguration scratchSpaceConf = baseScratchConfiguration .createScratchSpaceConfiguration(runtimeId, nodeId, appIdString); this.spaceInstanceInfo = new SpaceInstanceInfo(appId, runtimeId, nodeId, scratchSpaceConf); } catch (ConfigurationException x) { ProActiveLogger.logImpossibleException(logger, x); close(); throw new ProActiveRuntimeException(x); } logger.debug("Initialized application node scratch space"); } public void close() throws FileSystemException { logger.debug("Closing application scratch space"); try { try { final int filesNumber = spaceFile.delete(Selectors.SELECT_ALL); logger.debug("Deleted " + filesNumber + " files in scratch application directory"); } finally { // just a hint spaceFile.close(); } } catch (org.apache.commons.vfs.FileSystemException e) { throw new FileSystemException(e); } logger.debug("Closed application scratch space"); } public synchronized DataSpacesURI getScratchForAO(Body body) throws FileSystemException { // TODO performance can be improved using more fine-grained synchronization final String aoid = Utils.getActiveObjectId(body); if (logger.isDebugEnabled()) logger.debug("Request for scratch for Active Object with id: " + aoid); final DataSpacesURI uri; if (!scratches.containsKey(aoid)) { try { // TODO we could use VFSSpacesMountManagerImpl for that if it returned FileObject, // so we can avoid unnecessarily double mounting resulting in opening, // closing and opening again the same file createEmptyDirectoryRelative(spaceFile, aoid).close(); // just a hint spaceFile.close(); } catch (org.apache.commons.vfs.FileSystemException x) { logger.error( String.format("Could not create directory for Active Object (id: %s) scratch", aoid), x); throw new FileSystemException(x); } uri = spaceInstanceInfo.getMountingPoint().withActiveObjectId(aoid); if (logger.isDebugEnabled()) logger.debug( String.format("Created scratch for Active Object with id: %s, URI: %s", aoid, uri)); scratches.put(aoid, uri); } else uri = scratches.get(aoid); return uri; } public SpaceInstanceInfo getSpaceInstanceInfo() { return spaceInstanceInfo; } public DataSpacesURI getSpaceMountingPoint() { return spaceInstanceInfo.getMountingPoint(); } } public synchronized void init(Node node, BaseScratchSpaceConfiguration conf) throws FileSystemException, ConfigurationException, IllegalStateException { logger.debug("Initializing node scratch space"); if (configured) { logger.error("Attempting to configure already configured node scratch space"); throw new IllegalStateException("Instance already configured"); } if (conf.getUrls() == null) { throw new ConfigurationException("No remote access URL defined in base scratch configuration"); } this.node = node; this.baseScratchConfiguration = conf; try { fileSystemManager = VFSFactory.createDefaultFileSystemManager(); } catch (org.apache.commons.vfs.FileSystemException x) { logger.error("Could not create and configure VFS manager", x); throw new FileSystemException(x); } try { final String nodeId = Utils.getNodeId(node); final String runtimeId = Utils.getRuntimeId(node); final String[] originalUrls = baseScratchConfiguration.getUrls(); // find the file url in the list (it should be the first one) URI fileUri = null; for (String url : originalUrls) { try { URI uri = new URI(url); if (uri.getScheme().equals("file")) { fileUri = uri; break; } } catch (URISyntaxException e) { logger.error("Could not initialize scratch space"); throw new FileSystemException(e); } } String localAccessUrl; if (fileUri != null) { // if a file url was among the list use it localAccessUrl = Utils.getLocalAccessURL((new File(fileUri)).getAbsolutePath(), baseScratchConfiguration.getPath(), Utils.getHostname()); } else { // otherwise compute a new url using the configuration root path localAccessUrl = Utils.getLocalAccessURL(originalUrls[0], baseScratchConfiguration.getPath(), Utils.getHostname()); } final String partialSpacePath = Utils.appendSubDirs(localAccessUrl, runtimeId, nodeId); logger.debug("Accessing scratch space location: " + partialSpacePath); try { partialSpaceFile = fileSystemManager.resolveFile(partialSpacePath); checkCapabilities(partialSpaceFile.getFileSystem()); partialSpaceFile.delete(Selectors.EXCLUDE_SELF); partialSpaceFile.createFolder(); if (!partialSpaceFile.isWriteable()) { throw new org.apache.commons.vfs.FileSystemException( "Created directory is unexpectedly not writable"); } // just a hint partialSpaceFile.close(); } catch (org.apache.commons.vfs.FileSystemException x) { logger.error("Could not initialize scratch space at: " + partialSpacePath); throw new FileSystemException(x); } configured = true; logger.debug("Initialized node scratch space at: " + partialSpacePath); } finally { if (!configured) fileSystemManager.close(); } } public synchronized ApplicationScratchSpace initForApplication(final long appId) throws FileSystemException, IllegalStateException { checkIfConfigured(); return new AppScratchSpaceImpl(appId); } public synchronized void close() throws IllegalStateException { logger.debug("Closing node scratch space"); checkIfConfigured(); try { final FileObject fRuntime = partialSpaceFile.getParent(); // rm -r node partialSpaceFile.delete(Selectors.SELECT_ALL); // try to remove runtime file // IMPORTANT FIXME: it seems that despite of VFS FileObject documentation, // looking at AbstractFileObject docs suggests that it does not implement this // delete-if-empty behavior! at least, it appears to be not atomic (and probably may be never atomic // as some protocols may not support this kind of atomic operation?) // refreshing file before deleting may minimize the risk of delete-when-non-empty behavior fRuntime.refresh(); try { final boolean deleted = fRuntime.delete(); if (deleted) logger.debug("Scratch directory for whole runtime was deleted (considered as empty)"); else logger.debug("Scratch directory for whole runtime was not deleted (not considered as empty)"); } catch (org.apache.commons.vfs.FileSystemException x) { logger.debug("Could not delete scratch directory for whole runtime - perhaps it was not empty", x); } // it is probably not needed to close files if manager is closed, but with VFS you never know... fRuntime.close(); partialSpaceFile.close(); } catch (org.apache.commons.vfs.FileSystemException x) { ProActiveLogger.logEatedException(logger, "Could not close correctly node scratch space", x); } finally { this.fileSystemManager.close(); } logger.debug("Closed node scratch space"); } private FileObject createEmptyDirectoryRelative(final FileObject parent, final String path) throws org.apache.commons.vfs.FileSystemException { FileObject f = parent.resolveFile(path); f.delete(Selectors.EXCLUDE_SELF); f.createFolder(); return f; } private void checkCapabilities(FileSystem fs) throws ConfigurationException { // let's have at least those capabilities that scratch space does // final Capability[] expected = PADataSpaces.getCapabilitiesForSpaceType(SpaceType.SCRATCH); // but you never know what is there.. therefore: final Capability[] expected = new Capability[] { Capability.CREATE, Capability.DELETE, Capability.GET_TYPE, Capability.LIST_CHILDREN, Capability.READ_CONTENT, Capability.WRITE_CONTENT }; for (int i = 0; i < expected.length; i++) { final Capability capability = expected[i]; if (fs.hasCapability(capability)) continue; logger.error("Scratch file system does not support capability: " + capability); throw new ConfigurationException("Scratch file system does not support capability: " + capability); } } private void checkIfConfigured() throws IllegalStateException { if (!configured) { logger.error("Attempting to perform operation on not configured node scratch space"); throw new IllegalStateException("Instance not configured"); } } }