/* * Copyright (c) 2012 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.common.headless.impl; import java.io.File; import java.io.FileFilter; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.UUID; import org.apache.commons.io.FileUtils; import org.joda.time.DateTime; import org.joda.time.ReadableDuration; import de.fhg.igd.slf4jplus.ALogger; import de.fhg.igd.slf4jplus.ALoggerFactory; import eu.esdihumboldt.hale.common.headless.WorkspaceService; import eu.esdihumboldt.util.PlatformUtil; import eu.esdihumboldt.util.PropertiesFile; /** * Default implementation of the {@link WorkspaceService}. * * @author Simon Templer */ public class WorkspaceServiceImpl implements WorkspaceService { /** * Prefix of property names that are part of the external workspace * settings. */ private static final String PROPERTY_SETTING_PREFIX = "setting_"; /** * Suffix of the configuration file for a workspace folder, that is placed * alongside it in {@link #parentDir}. */ private static final String CONFIG_FILE_SUFFIX = ".lease"; /** * The property in the configuration file that states the end date of the * workspace lease time. */ private static final String PROPERTY_LEASE_END = "leaseEnd"; private static final ALogger log = ALoggerFactory.getLogger(WorkspaceServiceImpl.class); private final File parentDir; /** * Create a workspace service instance. * * @param workspacesDir the base directory for workspaces, if the location * does not exist or is not accessible, a default location inside * the platform instance location is used */ public WorkspaceServiceImpl(File workspacesDir) { if (workspacesDir == null || !workspacesDir.exists()) { // use default location workspacesDir = PlatformUtil.getInstanceLocation(); if (workspacesDir != null) { if (!workspacesDir.exists()) { workspacesDir.mkdirs(); } } else { log.error("No instance location, can't initialize workspace service."); } } parentDir = workspacesDir; if (parentDir != null) { log.info("Workspaces location is " + parentDir.getAbsolutePath()); } } /** * @see WorkspaceService#leaseWorkspace(ReadableDuration) */ @Override public String leaseWorkspace(ReadableDuration duration) { File workspace = newFolder(); DateTime leaseEnd = DateTime.now().plus(duration); File configFile = configFile(workspace); PropertiesFile props; try { props = new PropertiesFile(configFile); props.setProperty(PROPERTY_LEASE_END, leaseEnd.toString()); props.save(); } catch (IOException e) { throw new IllegalStateException("Can't write to workspace folder", e); } log.info("Leasing workspace " + workspace.getName() + " until " + leaseEnd.toString()); return workspace.getName(); } /** * @see WorkspaceService#getWorkspaceFolder(String) */ @Override public File getWorkspaceFolder(String id) throws FileNotFoundException { if (id == null || id.isEmpty()) { throw new FileNotFoundException("Invalid workspace ID"); } File workspace = new File(parentDir, id); if (!workspace.exists() || !configFile(workspace).exists()) { throw new FileNotFoundException("Workspace does not exist"); } return workspace; } /** * @see WorkspaceService#getLeaseEnd(String) */ @Override public DateTime getLeaseEnd(String workspaceId) throws IOException { PropertiesFile config = getConfiguration(workspaceId); String leaseEnd = config.getProperty(PROPERTY_LEASE_END); return DateTime.parse(leaseEnd); } private PropertiesFile getConfiguration(String workspaceId) throws IOException { File configFile = configFile(getWorkspaceFolder(workspaceId)); if (configFile.exists()) { return new PropertiesFile(configFile); } throw new FileNotFoundException("Workspace configuration file missing"); } /** * @see WorkspaceService#getSettings(String) */ @Override public Map<String, String> getSettings(String workspaceId) throws IOException { PropertiesFile config; synchronized (this) { // TODO better locking, read/write, based in // workspaceId?! config = getConfiguration(workspaceId); } @SuppressWarnings("unchecked") Enumeration<String> enProps = (Enumeration<String>) config.propertyNames(); Map<String, String> result = new HashMap<String, String>(); while (enProps.hasMoreElements()) { String property = enProps.nextElement(); if (property.startsWith(PROPERTY_SETTING_PREFIX)) { result.put(property.substring(PROPERTY_SETTING_PREFIX.length()), config.getProperty(property)); } } return result; } /** * @see WorkspaceService#set(String, String, String) */ @Override public void set(String workspaceId, String setting, String value) throws IOException { synchronized (this) { // TODO better locking, read/write, based on // workspaceId?! PropertiesFile config = getConfiguration(workspaceId); String key = PROPERTY_SETTING_PREFIX + setting; if (value == null) { config.remove(key); } else { config.setProperty(key, value); } config.save(); } } /** * @see WorkspaceService#deleteWorkspace(String) */ @Override public void deleteWorkspace(String id) { try { File workspace = getWorkspaceFolder(id); // delete folder FileUtils.deleteDirectory(workspace); // delete configuration file configFile(workspace).delete(); log.info("Removed workspace " + workspace.getName() + "."); } catch (IOException e) { log.error("Error deleting workspace folder", e); } } /** * Triggers the service scanning for workspace folders where the lease time * has ended and deletes them. */ public void trigger() { for (File candidate : parentDir.listFiles(new FileFilter() { @Override public boolean accept(File file) { return !file.isHidden() && file.isDirectory() && configFile(file).exists(); } })) { // retrieve lease end time DateTime leaseEnd = null; try { PropertiesFile configFile = new PropertiesFile(configFile(candidate)); leaseEnd = DateTime.parse(configFile.getProperty(PROPERTY_LEASE_END)); } catch (Exception e) { log.error("Failed to retrieve workspace folder lease end time.", e); } if (leaseEnd != null) { if (leaseEnd.isBeforeNow()) { // delete folder try { FileUtils.deleteDirectory(candidate); } catch (IOException e) { log.error("Error deleting workspace folder", e); } if (candidate.exists()) { log.error("Failed to delete workspace folder, leaving it for next time."); } else { configFile(candidate).delete(); log.info("Removed workspace " + candidate.getName() + " after lease expired."); } } } } } private File configFile(File workspace) { return new File(workspace.getParentFile(), workspace.getName() + CONFIG_FILE_SUFFIX); } private synchronized File newFolder() { File folder = null; while (folder == null || folder.exists()) { folder = new File(parentDir, UUID.randomUUID().toString()); } folder.mkdirs(); return folder; } }