/* * Copyright (C) 2008 Universidade Federal de Campina Grande * * This file is part of OurGrid. * * OurGrid is free software: you can redistribute it and/or modify it 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. * * 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 Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package org.ourgrid.worker.business.controller; import java.io.File; import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.List; import org.ourgrid.common.exception.UnableToDigestFileException; import org.ourgrid.common.internal.IResponseTO; import org.ourgrid.common.internal.response.LoggerResponseTO; import org.ourgrid.common.util.JavaFileUtil; import org.ourgrid.reqtrace.Req; import org.ourgrid.worker.WorkerConstants; import org.ourgrid.worker.business.dao.WorkerDAOFactory; import org.ourgrid.worker.business.exception.UnableToCreatePlaypenException; import org.ourgrid.worker.business.exception.UnableToCreateStorageException; import org.ourgrid.worker.business.messages.EnvironmentControllerMessages; import org.ourgrid.worker.business.messages.WorkerControllerMessages; import org.ourgrid.worker.utils.RandomNumberUtil; import br.edu.ufcg.lsd.commune.network.signature.Util; /** * Controls this worker's environment. */ public class EnvironmentController { private static EnvironmentController instance; @Req("REQ079") /** * Builds a new Environment Controller. */ public static synchronized EnvironmentController getInstance() { if (instance == null) { instance = new EnvironmentController(); } return instance; } @Req("REQ079") /** * Creates the playpen directory. This is a random directory, * so the chance the same directory is picked twice is very low. * @throw UnableToCreatePlaypenException In case the playpen root cannot be created * or is read-only; or the directory picked already exists, cannot be created or is read-only. */ public void mountPlaypen(String playpenRoot) throws UnableToCreatePlaypenException { String playpenDirPath = playpenRoot + File.separator + generatePlaypenDir(); File playpenRootFile = new File(playpenRoot); if(!playpenRootFile.exists()) { boolean rootSuccessfulCreation = playpenRootFile.mkdirs(); if(!rootSuccessfulCreation) { throw new UnableToCreatePlaypenException(playpenDirPath); } } else { if(!playpenRootFile.canWrite()) { throw new UnableToCreatePlaypenException(playpenDirPath); } } File playpenDir = new File(playpenDirPath); if(playpenDir.exists()) { throw new UnableToCreatePlaypenException(playpenDirPath); } boolean dirSuccessfulCreation = playpenDir.mkdir(); if(!dirSuccessfulCreation) { throw new UnableToCreatePlaypenException(playpenDirPath); } if(!playpenDir.canWrite()) { throw new UnableToCreatePlaypenException(playpenDirPath); } WorkerDAOFactory.getInstance().getEnvironmentDAO().setPlaypenDir(playpenDirPath); } @Req("REQ079") /** * Destroys the playpen directory. * This operation is also known as <code>WORKER CLEANING</code>. */ public void unmountPlaypen(List<IResponseTO> responses) { try { String playpenDir = WorkerDAOFactory.getInstance().getEnvironmentDAO().getPlaypenDir(); WorkerDAOFactory.getInstance().getEnvironmentDAO().setPlaypenDir(null); if (playpenDir == null) { return; } responses.add(new LoggerResponseTO(WorkerControllerMessages.getCleaningWorkerMessage(), LoggerResponseTO.DEBUG)); boolean successfulDestruction = destroyPlaypen(playpenDir); if(!successfulDestruction) { responses.add(new LoggerResponseTO(EnvironmentControllerMessages .getUnsuccesfulPlaypenDirDeletionMessage(playpenDir), LoggerResponseTO.ERROR)); } } catch (IOException e) { responses.add(new LoggerResponseTO(EnvironmentControllerMessages .getUnsuccesfulPlaypenDirDeletionMessage(WorkerDAOFactory.getInstance().getEnvironmentDAO().getPlaypenDir()), LoggerResponseTO.ERROR, e)); } } @Req("REQ082") /** * Unmounts the playpen and sets the storage directory to <code>null</code>. */ public void unmountEnvironment(List<IResponseTO> responses) { unmountPlaypen(responses); WorkerDAOFactory.getInstance().getEnvironmentDAO().setStorageDir(null); } @Req({"REQ079", "REQ082"}) /** * Creates the storage directory. This directory is specific for * every consumer this worker has worked for, and the chance it can * be repeated is very low. That means the directory will be the same * every time this worker works for the same consumer. * @throws UnableToCreateStorageException In case the storage directory cannot be determined, * or either the storage root or the storage directory cannot be created or is read-only. */ public void mountStorage(String consumerPubKey, String storageRoot) throws UnableToCreateStorageException { String storageDirPath; try { storageDirPath = storageRoot + File.separator + generateStorageDir(consumerPubKey); } catch (NoSuchAlgorithmException e) { throw new UnableToCreateStorageException(null); } File storageRootFile = new File(storageRoot); if(!storageRootFile.exists()) { boolean rootSuccessfulCreation = storageRootFile.mkdirs(); if(!rootSuccessfulCreation) { throw new UnableToCreateStorageException(storageDirPath); } } else { if(!storageRootFile.canWrite()) { throw new UnableToCreateStorageException(storageDirPath); } } File storageDir = new File(storageDirPath); if(!storageDir.exists()) { boolean dirSuccessfulCreation = storageDir.mkdir(); if(!dirSuccessfulCreation) { throw new UnableToCreateStorageException(storageDirPath); } } if(!storageDir.canWrite()) { throw new UnableToCreateStorageException(storageDirPath); } WorkerDAOFactory.getInstance().getEnvironmentDAO().setStorageDir(storageDir.getAbsolutePath()); } private String generateStorageDir(String stringToBeHashed) throws NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance("MD5"); digest.update(stringToBeHashed.getBytes()); byte[] hashedKey = digest.digest(); return Util.encodeArrayToHexadecimalString(hashedKey); } @Req("REQ079") private boolean destroyPlaypen(String playpenPath) throws IOException { boolean successful = true; File file = new File(playpenPath); if(file.exists() && file.isDirectory()) { successful = successful && deleteFilesInDir(file); successful = successful && file.delete(); } return successful; } /** * Removes all the files in a specified directory. * * @param directory The directory from which all the files will be removed. * @throws IOException if any I/O problem happens in the deletion process. */ @Req("REQ079") private boolean deleteFilesInDir(File directory) throws IOException { boolean successful = true; File[] files = directory.listFiles(); if (files != null) { for (File file : files) { if (file.isDirectory()) { successful = successful && deleteFilesInDir(file); } successful = successful && file.delete(); } } return successful; } @Req("REQ079") private String generatePlaypenDir() { long randomNumber = (long) (RandomNumberUtil.getRandom() * Long.MAX_VALUE); randomNumber = Long.signum(randomNumber) == -1 ? randomNumber * (-1) : randomNumber; return "worker-" + randomNumber; } @Req({"REQ082"}) public String solveStorageDir(String filePath) throws IOException { return solveDir(filePath, WorkerConstants.ENV_STORAGE, WorkerDAOFactory.getInstance().getEnvironmentDAO().getStorageDir()); } @Req("REQ080") public String solvePlaypenDir(String filePath) throws IOException { return solveDir(filePath, WorkerConstants.ENV_PLAYPEN, WorkerDAOFactory.getInstance().getEnvironmentDAO().getPlaypenDir()); } @Req("REQ082") public String getFileDigest(String filePath) throws UnableToDigestFileException { File file = new File(filePath); if(!file.exists() || file.isDirectory()) { return "0"; // Digest from a directory. } return JavaFileUtil.getDigestRepresentation(file); } @Req("REQ080") public String solveDir(String filePath) throws IOException { if (filePath != null && filePath.contains("$" + WorkerConstants.ENV_STORAGE)) { return solveStorageDir(filePath); } return solvePlaypenDir(filePath); } @Req({"REQ080", "REQ082"}) public String solveDir(String filePath, String relativePath, String absolutePath) throws IOException { if(filePath == null) { throw new IOException(EnvironmentControllerMessages.getNullFilePathMessage()); } if(!checkFilePathVar(filePath, relativePath)) { throw new IOException(EnvironmentControllerMessages.getInvalidVariableFoundMessage()); } String solvedPath = null; if (filePath.indexOf("$") == 0) { solvedPath = filePath.replace("$" + relativePath, absolutePath); } else { /* * Any relative or absolute path will become relative to the * variable value */ solvedPath = absolutePath + File.separator + filePath; } if (!isRelativeTo(solvedPath, absolutePath)) { throw new IOException(EnvironmentControllerMessages.getNotRelativeFilePathMessage(absolutePath)); } File file = new File(solvedPath); if(file.exists() && !file.canRead()) { throw new IOException(EnvironmentControllerMessages.getUnreadableFileInfoMessage()); } return file.getCanonicalPath(); } /** * If file path does not contain any $VARIABLE, return <code>true</code>; * If file path contains a $VARIABLE, it has to start with $ALLOWED_VARIABLE and cannot * contain any additional variable. */ @Req("REQ080") private boolean checkFilePathVar(String filePath, String allowedVariable) { int firstDollar = filePath.indexOf("$"); if(firstDollar == -1){ return true; } return filePath.startsWith("$" + allowedVariable) && (firstDollar == filePath.lastIndexOf("$")); } @Req("REQ080") private boolean isRelativeTo(String filePath, String directory) throws IOException { final String fileCanPath = new File(filePath).getCanonicalPath(); final String dirCanPath = new File(directory).getCanonicalPath(); return fileCanPath.startsWith(dirCanPath); } public boolean isCleaning(String playpenRoot) { File playpenRootFile = new File(playpenRoot); if (!playpenRootFile.isDirectory()) { return true; } return playpenRootFile.listFiles().length == 0; } }