br.unb.cic.bionimbuz.services.storage.StorageService.java Source code

Java tutorial

Introduction

Here is the source code for br.unb.cic.bionimbuz.services.storage.StorageService.java

Source

/*
 * BioNimbuZ is a federated cloud platform.
 * Copyright (C) 2012-2015 Laboratory of Bioinformatics and Data (LaBiD),
 * Department of Computer Science, University of Brasilia, Brazil
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package br.unb.cic.bionimbuz.services.storage;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.avro.AvroRemoteException;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.codahale.metrics.MetricRegistry;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpException;

import br.unb.cic.bionimbuz.avro.gen.FileInfo;
import br.unb.cic.bionimbuz.avro.gen.NodeInfo;
import br.unb.cic.bionimbuz.avro.rpc.AvroClient;
import br.unb.cic.bionimbuz.avro.rpc.RpcClient;
import br.unb.cic.bionimbuz.config.BioNimbusConfig;
import br.unb.cic.bionimbuz.plugin.PluginFile;
import br.unb.cic.bionimbuz.plugin.PluginInfo;
import br.unb.cic.bionimbuz.security.HashUtil;
import br.unb.cic.bionimbuz.security.Integrity;
import br.unb.cic.bionimbuz.services.AbstractBioService;
import br.unb.cic.bionimbuz.services.UpdatePeerData;
import br.unb.cic.bionimbuz.services.messaging.CloudMessageService;
import br.unb.cic.bionimbuz.services.messaging.CuratorMessageService.Path;
import br.unb.cic.bionimbuz.services.sched.SchedService;
import br.unb.cic.bionimbuz.services.storage.bandwidth.BandwidthCalculator;
import br.unb.cic.bionimbuz.services.storage.policy.StoragePolicy;
import br.unb.cic.bionimbuz.services.storage.policy.impl.BioCirrusPolicy;
import br.unb.cic.bionimbuz.toSort.Listeners;
import br.unb.cic.bionimbuz.utils.Nmap;
import br.unb.cic.bionimbuz.utils.Put;

@Singleton
public class StorageService extends AbstractBioService {

    private static final Logger LOGGER = LoggerFactory.getLogger(StorageService.class);

    private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1,
            new BasicThreadFactory.Builder().namingPattern("StorageService-%d").build());

    private Map<String, PluginInfo> cloudMap = new ConcurrentHashMap<>();
    private final Map<String, PluginFile> savedFiles = new ConcurrentHashMap<>();
    // private Set<String> pendingSaveFiles = new HashSet<String>();
    private final File dataFolder = new File(BioNimbusConfig.get().getDataFolder());
    private final Double MAXCAPACITY = 0.9;
    private final int PORT = 8080;
    private final int REPLICATIONFACTOR = 2;

    @Inject
    public StorageService(final CloudMessageService cms, MetricRegistry metricRegistry) {

        Preconditions.checkNotNull(cms);
        this.cms = cms;
    }

    @Override
    public void run() {
    }

    /**
     * Mtodo que inicia a storage
     *
     * @param config
     * @param listeners
     */
    @Override
    public void start(List<Listeners> listeners) {
        this.listeners = listeners;
        if (listeners != null) {
            listeners.add(this);
        }
        // Criando pastas zookeeper para o mdulo de armazenamento
        if (!this.cms.getZNodeExist(Path.PENDING_SAVE.getFullPath(), null)) {
            this.cms.createZNode(CreateMode.PERSISTENT, Path.PENDING_SAVE.getFullPath(), null);
        }
        if (!this.cms.getZNodeExist(Path.FILES.getFullPath(BioNimbusConfig.get().getId()), null)) {
            this.cms.createZNode(CreateMode.PERSISTENT, Path.FILES.getFullPath(BioNimbusConfig.get().getId()), "");
        }

        // watcher para verificar se um pending_save foi lanado
        this.cms.getChildren(Path.PENDING_SAVE.getFullPath(), new UpdatePeerData(this.cms, this, null));
        this.cms.getChildren(Path.PEERS.getFullPath(), new UpdatePeerData(this.cms, this, null));

        // NECESSARIO atualizar a lista de arquivo local , a lista do zookeeper com os arquivos locais.
        // checkFiles();
        this.checkPeers();
        try {
            if (this.getPeers().size() != 1) {
                this.checkReplicationFiles();
            }
        } catch (final Exception ex) {
            LOGGER.error("[Exception] - " + ex.getMessage());
        }
        this.executorService.scheduleAtFixedRate(this, 0, 1, TimeUnit.MINUTES);
    }

    @Override
    public void shutdown() {
        this.listeners.remove(this);
        this.executorService.shutdownNow();
    }

    @Override
    public void getStatus() {
        // TODO Auto-generated method stub
    }

    /**
     * Verifica os peers(plugins) existentes e adiciona um observador(watcher)
     * no zNode STATUS de cada plugin.
     */
    public void checkPeers() {
        for (final PluginInfo plugin : this.getPeers().values()) {
            if (this.cms.getZNodeExist(Path.STATUS.getFullPath(plugin.getId()), null)) {
                this.cms.getData(Path.STATUS.getFullPath(plugin.getId()), new UpdatePeerData(this.cms, this, null));
            }
        }
    }

    /**
     * Verifica os arquivos que existem no recurso e adiciona no Zookeeper os arquivos novos. Alterado para synchronized
     * para evitar condio de corrida.
     */
    public synchronized void checkFiles() {
        try {
            if (!this.dataFolder.exists()) {
                this.dataFolder.mkdirs();
            }
            this.cms.getChildren(Path.FILES.getFullPath(BioNimbusConfig.get().getId()),
                    new UpdatePeerData(this.cms, this, null));
            for (final File file : this.dataFolder.listFiles()) {
                if (!this.savedFiles.containsKey(file.getName())) {
                    final PluginFile pluginFile = new PluginFile();
                    pluginFile.setId(file.getName());
                    pluginFile.setName(file.getName());
                    pluginFile.setPath(file.getPath());

                    final List<String> listIds = new ArrayList<>();
                    listIds.add(BioNimbusConfig.get().getId());

                    pluginFile.setPluginId(listIds);
                    pluginFile.setSize(file.length());
                    // pluginFile.setHash(Hash.calculateSha3(file.getPath()));
                    // cria um novo znode para o arquivo e adiciona o watcher
                    this.cms.createZNode(CreateMode.PERSISTENT,
                            Path.NODE_FILE.getFullPath(BioNimbusConfig.get().getId(), pluginFile.getId()),
                            pluginFile.toString());
                    this.cms.getData(Path.NODE_FILE.getFullPath(BioNimbusConfig.get().getId(), pluginFile.getId()),
                            new UpdatePeerData(this.cms, this, null));

                    this.savedFiles.put(pluginFile.getName(), pluginFile);
                }

            }
        } catch (final Exception ex) {
            LOGGER.error("[Exception] - " + ex.getMessage());
        }
    }

    /**
     * Checa quantas cpias existem de um arquivo, caso existam menos cpias do
     * que REPLICATIONFACTOR inicia a replicao deste arquivo; Este mtodo
     * checa todos os arquivos da federao.
     *
     * @throws Exception
     */
    public void checkReplicationFiles() throws Exception {
        for (final Collection<String> collection : this.getFiles().values()) {
            /*
             * Percorre cada arquivo e o IP que possui ele
             */
            for (final String fileNamePlugin : collection) {
                if (!this.existReplication(fileNamePlugin)) {
                    /*
                     * Caso no exista um nmero de cpias igual a REPLICATIONFACTOR inicia as cpias,
                     * enviando uma RPC para o peer que possui o arquivo, para que ele replique.
                     */
                    final String ipPluginFile = this.getIpContainsFile(fileNamePlugin);
                    if (!ipPluginFile.isEmpty() && !ipPluginFile.equals(BioNimbusConfig.get().getAddress())) {
                        final RpcClient rpcClient = new AvroClient("http", ipPluginFile, this.PORT);
                        rpcClient.getProxy().notifyReply(fileNamePlugin, ipPluginFile);
                        rpcClient.close();
                    } else {
                        this.replication(fileNamePlugin, ipPluginFile);
                    }
                }
            }
        }
    }

    /**
     * Verifica a existncia da replicao do arquivo na federao. Se a
     * replicao estiver feita retona true; Fator de replicao igual a 2;
     * Retorna true se existir replicao.
     */
    private boolean existReplication(String fileName) throws IOException {
        int cont = 0;
        for (final Collection<String> collection : this.getFiles().values()) {
            for (final String fileNamePlugin : collection) {
                if (fileName.equals(fileNamePlugin)) {
                    cont++;
                }
            }
        }
        return cont >= this.REPLICATIONFACTOR;
    }

    /**
     * Cria map com endereo dos peers(plugins) e seus respectivos arquivos
     * baseado nos dados do zookeeper.
     *
     * @return map de endereo e lista de arquivos.
     * @throws java.io.IOException
     */
    public Map<String, List<String>> getFiles() throws IOException {
        final Map<String, List<String>> mapFiles = new HashMap<>();
        List<String> listFiles;
        this.checkFiles();

        for (final PluginInfo plugin : this.getPeers().values()) {
            listFiles = new ArrayList<>();
            for (final String file : this.cms.getChildren(Path.FILES.getFullPath(plugin.getId()),
                    new UpdatePeerData(this.cms, this, null))) {
                listFiles.add(file);
            }
            mapFiles.put(plugin.getHost().getAddress(), listFiles);
        }

        return mapFiles;

    }

    /**
     * Metodo para pegar o Ip de cada peer na federao e verificar em qual peer
     * o arquivo est, se o arquivo for encontrado retorna o Ip do peer, caso
     * contrrio retorna null.
     *
     * @param file
     * @return Ip que possui o arquivo ou null
     * @throws java.io.IOException
     */
    public String getIpContainsFile(String file) throws IOException {
        List<String> listFiles;
        // NECESSARIO atualizar a lista de arquivo local , a lista do zookeeper com os arquivos locais. No  feito em nenhum momento
        // caso no seja chamado a checkFiles();
        this.checkFiles();

        for (final Iterator<PluginInfo> it = this.getPeers().values().iterator(); it.hasNext();) {
            final PluginInfo plugin = it.next();
            listFiles = this.cms.getChildren(Path.FILES.getFullPath(plugin.getId()), null);
            for (final String checkfile : listFiles) {
                if (file.equals(checkfile)) {
                    return plugin.getHost().getAddress();
                }
            }
        }
        return "";
    }

    /**
     * Retorna o tamanho do arquivo, dado o nome do mesmo.
     * NOTE: listFiles never used. Revise this code.
     * Refactor message of integrity verification
     *
     * @param file
     *            O nome do arquivo
     * @return O tamanho do arquivo
     */
    public long getFileSize(String file) {

        try {
            for (final Iterator<PluginInfo> it = this.getPeers().values().iterator(); it.hasNext();) {
                final PluginInfo plugin = it.next();
                final PluginFile files = new ObjectMapper().readValue(
                        this.cms.getData(Path.NODE_FILE.getFullPath(plugin.getId(), file), null), PluginFile.class);
                return files.getSize();
            }
        } catch (final IOException ex) {
            LOGGER.error("[IOException] - " + ex.getMessage());
        }
        return 0;
    }

    /**
     * Recebe uma list com todos os peers da federao e seta o custo de
     * armazenamento em cada plugin
     *
     * @param list
     *            - Lista com todos os plugins da federao
     * @return - Lista com todos os plugins com seus custos de armazenamento
     *         inseridos
     */
    public List<NodeInfo> bestNode(List<NodeInfo> list) {

        List<NodeInfo> plugins;
        this.cloudMap = this.getPeers();
        for (final NodeInfo node : list) {
            this.cloudMap.get(node.getPeerId()).setLatency(node.getLatency());
            this.cloudMap.get(node.getPeerId()).setFsFreeSize(node.getFreesize());
        }
        // TODO: Permitir a escolha entre a BioCirrus e a ZooClouS
        final StoragePolicy policy = new BioCirrusPolicy();
        /*
         * Dentro da Storage Policy  feito o ordenamento da list de acordo com o custo de armazenamento
         */
        plugins = policy.calcBestCost(this.cms, this.cloudMap.values());

        return plugins;
    }

    /**
     * Verifica se um arquivo existe em um peer e seta o seu Znode no Zookeeper
     *
     * @param file
     *            - Arquivo a ser verifcado
     * @return true caso o arquivo exista e tenha sido setado
     */
    public boolean checkFilePeer(PluginFile file) {
        LOGGER.info("Verifying if file (filename=" + file.getName() + ") exists on peer");

        final File localFile = new File(BioNimbusConfig.get() + file.getName());

        if (localFile.exists()) {
            this.cms.createZNode(CreateMode.PERSISTENT,
                    Path.NODE_FILE.getFullPath(BioNimbusConfig.get().getId(), file.getId()), file.toString());
            this.cms.getData(Path.NODE_FILE.getFullPath(BioNimbusConfig.get().getId(), file.getId()),
                    new UpdatePeerData(this.cms, this, null));
            return true;
        }

        LOGGER.info("File not found on Peer");
        return false;
    }

    /**
     * Mtodo que manda o comando dizendo que o arquivo foi upado com o intuito
     * de replicar esse arquivo pelos ns.
     *
     * @param fileUploaded
     * @return
     * @throws KeeperException
     * @throws InterruptedException
     * @throws IOException
     * @throws java.security.NoSuchAlgorithmException
     * @throws com.jcraft.jsch.SftpException
     */
    public synchronized String fileUploaded(PluginFile fileUploaded)
            throws KeeperException, InterruptedException, IOException, NoSuchAlgorithmException, SftpException {
        LOGGER.info("Checking if there is request on PENDING_SAVE: " + fileUploaded.getName());

        Boolean successUpload = false;
        if (this.cms.getZNodeExist(Path.NODE_PENDING_FILE.getFullPath(fileUploaded.getId()), null)) {
            String ipPluginFile;
            ipPluginFile = this.getIpContainsFile(fileUploaded.getName());
            final FileInfo file = new FileInfo();
            file.setId(fileUploaded.getId());
            file.setName(fileUploaded.getName());
            file.setSize(fileUploaded.getSize());
            file.setHash(fileUploaded.getHash());
            String idPluginFile = null;

            for (final String idPlugin : fileUploaded.getPluginId()) {
                idPluginFile = idPlugin;
                break;
            }

            // Verifica se a mquina que recebeu essa requisio no  a que est armazenando o arquivo
            if (!fileUploaded.getPluginId().contains(BioNimbusConfig.get().getId())) {
                for (final PluginInfo plugin : this.getPeers().values()) {
                    if (plugin.getId().equals(fileUploaded.getPluginId().get(0))) {
                        ipPluginFile = plugin.getHost().getAddress();
                    }
                }
                final RpcClient rpcClient = new AvroClient("http", ipPluginFile, this.PORT);
                final String filePeerHash = rpcClient.getProxy().getFileHash(fileUploaded.getName());

                // Verifica se o arquivo foi corretamente transferido ao n.
                if (Integrity.verifyHashes(filePeerHash, fileUploaded.getHash())) {
                    successUpload = true;
                    try {
                        if (rpcClient.getProxy().verifyFile(file, fileUploaded.getPluginId())
                                && this.cms.getZNodeExist(
                                        Path.NODE_FILE.getFullPath(idPluginFile, fileUploaded.getId()), null)) {
                            // Remova o arquivo do PENDING FILE j que ele foi upado
                            this.cms.delete(Path.NODE_PENDING_FILE.getFullPath(fileUploaded.getId()));
                        }
                        rpcClient.close();
                    } catch (final Exception ex) {
                        LOGGER.error("[Exception] - " + ex.getMessage());
                    }
                }
            } else {
                if (this.checkFilePeer(fileUploaded)) {
                    final String filePeerHash = HashUtil
                            .computeNativeSHA3(BioNimbusConfig.get().getDataFolder() + fileUploaded.getName());

                    // Verifica se o arquivo foi corretamente transferido ao peer.
                    if (Integrity.verifyHashes(filePeerHash, fileUploaded.getHash())) {
                        successUpload = true;
                        if (this.cms.getZNodeExist(Path.NODE_FILE.getFullPath(idPluginFile, fileUploaded.getId()),
                                null) && !this.existReplication(file.getName())) {
                            try {
                                this.replication(file.getName(), BioNimbusConfig.get().getAddress());
                                if (this.existReplication(file.getName())) {
                                    // Remova o arquivo do PENDING FILE j que ele foi upado
                                    this.cms.delete(Path.NODE_PENDING_FILE.getFullPath(fileUploaded.getId()));
                                } else {
                                    LOGGER.info("Replication failed! File wasn't replicated...");
                                }
                            } catch (final JSchException ex) {
                                LOGGER.error("[JSchException] - " + ex.getMessage());
                            }
                        }
                    }
                }
            }
        } else {
            LOGGER.info("File not found on pending files");
        }

        if (successUpload) {
            LOGGER.info("File integrity verified: File uploaded correctly");

            return "File integrity verified: File uploaded correctly.";
        } else {
            LOGGER.error("File integrity verified: Error on file uploading.");

            return "File integrity verified: Error on file uploading.";
        }
    }

    /**
     * Metodo que checa os znodes filhos da pending_save, para replica-ls
     *
     * @throws java.io.IOException
     * @throws java.security.NoSuchAlgorithmException
     * @throws InterruptedException
     */
    public void checkingPendingSave() throws IOException, NoSuchAlgorithmException, InterruptedException {

        final ObjectMapper mapper = new ObjectMapper();
        int cont = 0;
        final List<String> pendingSave = this.cms.getChildren(Path.PENDING_SAVE.getFullPath(), null);
        // pendingSaveFiles.addAll(pendingSave);
        for (final String files : pendingSave) {
            try {
                final String data = this.cms
                        .getData(Path.NODE_PENDING_FILE.getFullPath(files.substring(13, files.length())), null);
                // verifica se arquivo existe
                if (data == null || data.trim().isEmpty()) {
                    LOGGER.info("========> There is no data for Path: " + Path.PENDING_SAVE.getFullPath());

                    continue;
                }
                final PluginFile fileplugin = mapper.readValue(data, PluginFile.class);

                // Verifica se  um arquivo de sada de uma execuo e se o arquivo foi gerado nesse recurso
                if (fileplugin.getService() != null
                        && fileplugin.getService().equals(SchedService.class.getSimpleName())
                        && fileplugin.getPluginId().get(0).equals(BioNimbusConfig.get().getId())) {
                    // Adiciona o arquivo a lista do zookeeper
                    this.checkFiles();
                }
                while (cont < 6) {
                    if (fileplugin.getPluginId().size() == this.REPLICATIONFACTOR) {
                        this.cms.delete(Path.PENDING_SAVE.getFullPath(fileplugin.getId()));
                        break;
                    }
                    final String address = this.getIpContainsFile(fileplugin.getName());
                    if (!address.isEmpty() && !address.equals(BioNimbusConfig.get().getAddress())) {
                        final RpcClient rpcClient = new AvroClient("http", address, this.PORT);
                        rpcClient.getProxy().notifyReply(fileplugin.getName(), address);
                        try {
                            rpcClient.close();
                            if (this.existReplication(fileplugin.getName())) {
                                this.cms.delete(Path.NODE_PENDING_FILE.getFullPath(fileplugin.getId()));
                                break;
                            } else {
                                cont++;
                            }
                        } catch (final Exception ex) {
                            LOGGER.error("[Exception] - " + ex.getMessage());
                        }
                    } else {
                        try {
                            this.replication(fileplugin.getName(), address);
                        } catch (final JSchException ex) {
                            LOGGER.error("[JSchException] - " + ex.getMessage());
                        } catch (final SftpException ex) {
                            LOGGER.error("[SftpException] - " + ex.getMessage());
                        }
                        if (this.existReplication(fileplugin.getName())) {
                            this.cms.delete(Path.NODE_PENDING_FILE.getFullPath(fileplugin.getId()));
                            break;
                        }
                    }
                }
            } catch (final IOException ex) {
                LOGGER.error("[IOException] - " + ex.getMessage());
            }
            // verifica se exite replicao quando houver mais de um peer
            if (this.getPeers().size() != 1) {
                this.existReplication(files);
            }
        }
    }

    /**
     * Realiza a replicao de arquivos, sejam eles enviados pelo cliente ou
     * apenas gerados na prpria federao
     *
     * @param filename
     *            - nome do arquivo
     * @param address
     *            - endereo do peer que possui o arquivo
     * @throws IOException
     * @throws JSchException
     * @throws SftpException
     * @throws java.io.FileNotFoundException
     * @throws java.security.NoSuchAlgorithmException
     * @throws InterruptedException
     */
    public synchronized void replication(String filename, String address) throws IOException, JSchException,
            SftpException, FileNotFoundException, NoSuchAlgorithmException, InterruptedException {
        LOGGER.info("Replicating file (filename=" + filename + ") from peer (peer=" + address + ")");

        List<NodeInfo> pluginList = new ArrayList<>();
        final List<String> idsPluginsFile = new ArrayList<>();
        final File file = new File(BioNimbusConfig.get().getDataFolder() + filename);

        final int filesreplicated = 1;

        // Verifica se o arquivo existe no peer
        if (file.exists()) {
            final FileInfo info = new FileInfo();
            info.setId(file.getName());
            info.setName(file.getName());
            info.setSize(file.length());
            info.setHash(HashUtil.computeNativeSHA3(file.getAbsolutePath()));

            final PluginFile pluginFile = new PluginFile(info);
            /*
             * PLuginList ira receber a lista dos Peers disponiveis na federao
             * e que possuem espao em disco para receber o arquivo a ser replicado
             */
            pluginFile.setPath(BioNimbusConfig.get().getDataFolder() + info.getName());
            NodeInfo no = null;

            /*
             * While para que o peer pegue o prprio endereo e ele seja removido da lista de peers,
             * isso  feito para evitar que ele tente replicar
             * o arquivo para ele mesmo.
             */
            for (final NodeInfo node : this.getNodeDisp(info.getSize())) {
                if (node.getAddress().equals(address)) {
                    no = node;
                    break;
                }
            }
            if (no != null) {
                pluginList.remove(no);
                idsPluginsFile.add(BioNimbusConfig.get().getId());
                pluginList = new ArrayList<>(this.bestNode(pluginList));
                for (final NodeInfo curr : pluginList) {
                    if (no.getAddress().equals(curr.getAddress())) {
                        no = curr;
                        break;
                    }
                }

                pluginList.remove(no);
            }
            pluginList = new ArrayList<>(this.bestNode(pluginList));
            pluginList.remove(no);
            final Iterator<NodeInfo> bt = pluginList.iterator();
            while (bt.hasNext() && filesreplicated != this.REPLICATIONFACTOR) {
                final NodeInfo node = bt.next();
                if (!node.getAddress().equals(address)) {
                    // Descoberto um peer disponivel, tenta enviar o arquivo
                    final Put conexao = new Put(node.getAddress(), this.dataFolder + "/" + info.getName());
                    if (conexao.startSession()) {
                        idsPluginsFile.add(node.getPeerId());

                        pluginFile.setPluginId(idsPluginsFile);

                        final RpcClient rpcClient = new AvroClient("http", node.getAddress(), this.PORT);
                        final String fileReplicatedHash = rpcClient.getProxy().getFileHash(pluginFile.getName());
                        // rpcClient.close();

                        // Verifica se o arquivo foi corretamente transferido ao n.
                        if (Integrity.verifyHashes(pluginFile.getHash(), fileReplicatedHash)) {
                            // Com o arquivo enviado, seta os seus dados no Zookeeper
                            for (final String idPlugin : idsPluginsFile) {
                                if (this.cms.getZNodeExist(Path.NODE_FILE.getFullPath(idPlugin, filename), null)) {
                                    this.cms.setData(Path.NODE_FILE.getFullPath(idPlugin, filename),
                                            pluginFile.toString());
                                } else {
                                    this.cms.createZNode(CreateMode.PERSISTENT,
                                            Path.NODE_FILE.getFullPath(idPlugin, filename), pluginFile.toString());
                                }
                                this.cms.getData(Path.NODE_FILE.getFullPath(idPlugin, filename),
                                        new UpdatePeerData(this.cms, this, null));
                            }
                        } else {
                            LOGGER.info("Error replicating the file to the peer (peer=" + node.getAddress() + ")");
                        }
                    }
                }
            }
        }
    }

    /**
     * Pega uma lista com todos os peers da federao e separa eles de acordo
     * com o tamanho do arquivo, criando uma lista somente com os peers que
     * possuem condies de receber o arquivo
     *
     * @param lengthFile
     * @return - Lista com peers que podem receber o arquivo
     */
    public List<NodeInfo> getNodeDisp(long lengthFile) {
        final List<NodeInfo> nodesdisp = new ArrayList<>();
        final Collection<PluginInfo> cloudPlugin = this.getPeers().values();
        nodesdisp.clear();
        for (final PluginInfo plugin : cloudPlugin) {
            try {
                final NodeInfo node = new NodeInfo();

                if ((long) (plugin.getFsFreeSize() * this.MAXCAPACITY) > lengthFile
                        && plugin.getId().equals(BioNimbusConfig.get().getId())) {
                    node.setLatency(Ping.calculo(plugin.getHost().getAddress()));
                    if (node.getLatency().equals(Double.MAX_VALUE)) {
                        node.setLatency(Nmap.nmap(plugin.getHost().getAddress()));
                    }
                    node.setBandwidth(
                            BandwidthCalculator.linkSpeed(plugin.getHost().getAddress(), node.getLatency()));
                    node.setAddress(plugin.getHost().getAddress());
                    node.setFreesize(plugin.getFsFreeSize());
                    node.setPeerId(plugin.getId());
                    nodesdisp.add(node);
                }
            } catch (final IOException ex) {
                LOGGER.error("[IOException] - " + ex.getMessage());
            } catch (final InterruptedException ex) {
                LOGGER.error("[InterruptedException] - " + ex.getMessage());
            }
        }
        return nodesdisp;
    }

    /**
     * Seta no Zookeeper os dados de um arquivo que foi requisitado por um
     * cliente para ser submetido na federao
     *
     * @param file
     *            - Arquivo a ser submetido
     */
    public void setPendingFile(PluginFile file) {
        this.cms.createZNode(CreateMode.PERSISTENT, Path.NODE_PENDING_FILE.getFullPath(file.getId()),
                file.toString());
    }

    /**
     * Cria uma Map com o ID de um peer e seus respectivos arquivos
     *
     * @param pluginId
     *            id do plugin para pegar os arquivos do plugin
     * @return Map com os plugins e seus arquivos
     */
    public List<PluginFile> getFilesPeer(String pluginId) {
        List<String> children;
        final List<PluginFile> filesPeerSelected = new ArrayList<>();
        // NECESSARIO atualizar a lista de arquivo local , a lista do zookeeper com os arquivos locais. No  feito em nenhum momento
        // caso no seja chamado a checkFiles();
        this.checkFiles();
        try {
            children = this.cms.getChildren(Path.FILES.getFullPath(pluginId), null);
            for (final String fileId : children) {
                final String fileName = fileId.substring(5, fileId.length());
                final ObjectMapper mapper = new ObjectMapper();
                final PluginFile file = mapper.readValue(
                        this.cms.getData(Path.NODE_FILE.getFullPath(pluginId, fileName), null), PluginFile.class);
                filesPeerSelected.add(file);
            }
        } catch (final IOException ex) {
            LOGGER.error("[IOException] - " + ex.getMessage());
        }

        return filesPeerSelected;
    }

    @Override
    public void verifyPlugins() {
        final Collection<PluginInfo> temp = this.getPeers().values();
        temp.removeAll(this.cloudMap.values());
        for (final PluginInfo plugin : temp) {
            if (this.cms.getZNodeExist(Path.STATUS.getFullPath(plugin.getId(), null, null), null)) {
                this.cms.getData(Path.STATUS.getFullPath(plugin.getId()), new UpdatePeerData(this.cms, this, null));
            }
        }
    }

    /**
     * Sends a file to ZooKeeper
     *
     * @param filepath
     * @param fileInfo
     * @return
     * @throws IOException
     * @throws InterruptedException
     * @throws JSchException
     * @throws java.security.NoSuchAlgorithmException
     * @throws SftpException
     */
    public boolean writeFileToZookeeper(String filepath, FileInfo fileInfo)
            throws IOException, JSchException, SftpException, NoSuchAlgorithmException, InterruptedException {

        List<NodeInfo> pluginList;
        List<NodeInfo> nodesdisp = new ArrayList<>();
        final RpcClient rpcClient = new AvroClient("http", BioNimbusConfig.get().getAddress(), 8080);
        final File file = new File(filepath);

        // Verifica se o arquivo existe
        if (file.exists()) {
            pluginList = rpcClient.getProxy().getPeersNode();

            // Insere o arquivo na pasta PENDING SAVE do Zookeeper
            rpcClient.getProxy().setFileInfo(fileInfo, "upload!");
            for (final Iterator<NodeInfo> it = pluginList.iterator(); it.hasNext();) {
                final NodeInfo plugin = it.next();

                // Adiciona na lista de possiveis peers de destino somente os que possuem espao livre para receber o arquivo
                if ((long) (plugin.getFreesize() * this.MAXCAPACITY) > fileInfo.getSize()) {
                    plugin.setLatency(Ping.calculo(plugin.getAddress()));
                    if (plugin.getLatency().equals(Double.MAX_VALUE)) {
                        plugin.setLatency(Nmap.nmap(plugin.getAddress()));
                    }
                    nodesdisp.add(plugin);
                }
            }

            // Retorna a lista dos nos ordenados como melhores, passando a latncia calculada
            nodesdisp = new ArrayList<>(rpcClient.getProxy().callStorage(nodesdisp));

            NodeInfo no = null;
            final Iterator<NodeInfo> it = nodesdisp.iterator();

            // Tenta enviar o arquivo para o melhor peer que est na lista
            while (it.hasNext() && no == null) {
                final NodeInfo node = it.next();
                final Put conexao = new Put(node.getAddress(), filepath);
                if (conexao.startSession()) {
                    no = node;
                    break;
                }
            }
            // Conserta o nome do arquivo encriptado
            // TODO: Remove comment after William Final Commit
            // final AESEncryptor aes = new AESEncryptor();
            // aes.setCorrectFilePath(path);
            if (no != null) {
                final List<String> dest = new ArrayList<>();
                dest.add(no.getPeerId());
                nodesdisp.remove(no);

                // Envia RPC para o peer em que est conectado, para que ele sete no Zookeeper os dados do arquivo que foi upado.
                rpcClient.getProxy().fileSent(fileInfo, dest);

                // File uploaded
                LOGGER.info("File uploaded!");
                return true;
            }

        }

        // Upload error
        LOGGER.error("File not found");

        return false;
    }

    /**
     * Cria Map com ID dos plugins e seus respectivos arquivos baseado nos dados
     * do zookeeper.
     *
     * @return Map<Id_Plugin, Lista de Arquivos>
     * @throws java.io.IOException
     */
    public Map<String, List<String>> getAllPluginFiles() throws IOException {
        final Map<String, List<String>> mapFiles = new HashMap<>();
        List<String> listFiles;
        this.checkFiles();

        for (final PluginInfo plugin : this.getPeers().values()) {
            listFiles = new ArrayList<>();
            for (final String file : this.cms.getChildren(Path.FILES.getFullPath(plugin.getId()),
                    new UpdatePeerData(this.cms, this, null))) {
                listFiles.add(file);
            }

            mapFiles.put(plugin.getId(), listFiles);
        }

        return mapFiles;

    }

    /**
     * Returns a FileInfo from a given filename. It searches over the ZooKeeper
     * structure to find the peer and the name.
     *
     * @param filename
     * @return
     */
    public br.unb.cic.bionimbuz.avro.gen.PluginFile getFileInfoByFilename(String filename) {
        try {
            // Map --> {peer_id, {file_1, file_2, file_3, ...}, peer_id, {}...}
            final Map<String, List<String>> map = this.getAllPluginFiles();

            // Iterates over the map
            for (final Map.Entry<String, List<String>> entry : map.entrySet()) {
                LOGGER.info("Iterating over peer: " + entry.getKey());

                // Iterates the list of files of a peer
                for (final String file : entry.getValue()) {
                    LOGGER.info("File: " + file);

                    // Verify if it is that searched
                    if (file.equals(filename)) {
                        // Creates Mapper
                        final ObjectMapper mapper = new ObjectMapper();

                        // Retrives from ZooKeeper
                        final String pFile = this.cms.getData(Path.NODE_FILE.getFullPath(entry.getKey(), file),
                                null);

                        // Convert to PluginFile
                        final br.unb.cic.bionimbuz.avro.gen.PluginFile pluginFile = mapper.readValue(pFile,
                                br.unb.cic.bionimbuz.avro.gen.PluginFile.class);

                        final br.unb.cic.bionimbuz.model.FileInfo fileInfo = new br.unb.cic.bionimbuz.model.FileInfo();
                        fileInfo.setId(pluginFile.getId());
                        fileInfo.setName(pluginFile.getName());
                        fileInfo.setHash(pluginFile.getHash());
                        fileInfo.setSize(pluginFile.getSize());
                        fileInfo.setUploadTimestamp("");

                        // Found -> return peer_id
                        return pluginFile;
                    }
                }
            }

        } catch (final IOException ex) {
            LOGGER.error("Error searching files");
            ex.printStackTrace();
        }

        return null;
    }

    /**
     * Mtodo que recebe um evento do zookeeper caso os znodes setados nessa
     * classe sofra alguma alterao, criado, deletado, modificado, trata os
     * eventos de acordo com o tipo do mesmo
     *
     * @param eventType
     */
    @Override
    public void event(WatchedEvent eventType) {
        final String path = eventType.getPath();
        switch (eventType.getType()) {

        case NodeChildrenChanged:
            if (eventType.getPath().equals(Path.PEERS.toString())) {

                if (this.cloudMap.size() < this.getPeers().size()) {
                    this.verifyPlugins();
                }
            } else if (eventType.getPath().equals(Path.PENDING_SAVE.toString())) {
                // chamada para checar a pending_save apenas quando uma alerta para ela for lanado
                // try{
                // checkingPendingSave();
                // }
                // catch (IOException ex) {
                // Logger.getLogger(StorageService.class.getName()).log(Level.SEVERE, null, ex);
                // }
            }
            break;
        case NodeDeleted:
            if (eventType.getPath().contains(Path.STATUS.toString())) {
                LOGGER.info("Erased ZNode Status");

                final String peerId = path.substring(12, path.indexOf("/STATUS"));
                if (this.getPeers().values().size() != 1) {
                    try {
                        if (!this.cms.getZNodeExist(Path.STATUSWAITING.getFullPath(peerId), null)) {
                            this.cms.createZNode(CreateMode.PERSISTENT, Path.STATUSWAITING.getFullPath(peerId), "");
                        }

                        final StringBuilder info = new StringBuilder(
                                this.cms.getData(Path.STATUSWAITING.getFullPath(peerId), null));
                        // verifica se recurso j foi recuperado ou est sendo recuperado por outro recurso
                        if (!info.toString().contains("S") /* && !info.toString().contains("L") */) {

                            // bloqueio para recuperar tarefas sem que outros recursos realizem a mesma operao
                            // cms.setData(Path.STATUSWAITING.getFullPath(peerId), info.append("L").toString());
                            // Verificar pluginid para gravar
                            for (final PluginFile fileExcluded : this.getFilesPeer(peerId)) {
                                String idPluginExcluded = null;
                                for (final String idPlugin : fileExcluded.getPluginId()) {
                                    if (peerId.equals(idPlugin)
                                            && !idPlugin.equals(BioNimbusConfig.get().getId())) {
                                        idPluginExcluded = idPlugin;
                                        break;
                                    }
                                }

                                if (fileExcluded.getPluginId().size() > 1) {
                                    fileExcluded.getPluginId().remove(idPluginExcluded);
                                }

                                this.setPendingFile(fileExcluded);
                                fileExcluded.setService("storagePeerDown");
                                this.fileUploaded(fileExcluded);
                            }

                            // retira bloqueio de uso e adiciona marcao de recuperao
                            // info.deleteCharAt(info.indexOf("L"));
                            info.append("S");
                            this.cms.setData(Path.STATUSWAITING.getFullPath(peerId), info.toString());

                            // nao  necessrio chamar esse mtodo aqui, ele ser chamado se for necessrio ao receber um alerta de watcher
                            // checkingPendingSave();
                        }

                    } catch (final AvroRemoteException ex) {
                        LOGGER.error("[AvroRemoteException] - " + ex.getMessage());
                    } catch (final KeeperException ex) {
                        LOGGER.error("[KeeperException] - " + ex.getMessage());
                    } catch (final InterruptedException ex) {
                        LOGGER.error("[InterruptedException] - " + ex.getMessage());
                    } catch (final IOException ex) {
                        LOGGER.error("[IOException] - " + ex.getMessage());
                    } catch (final NoSuchAlgorithmException ex) {
                        LOGGER.error("[NoSuchAlgorithmException] - " + ex.getMessage());
                    } catch (final SftpException ex) {
                        LOGGER.error("[SftpException] - " + ex.getMessage());
                    }
                }
                break;
            }
        default:
            break;
        }
    }
}