URingPaxos-master/clients/SMR/src/java/ch/usi/da/smr/recovery/DfsRecovery.java
package ch.usi.da.smr.recovery;
/* 
 * Copyright (c) 2014 Università della Svizzera italiana (USI)
 * 
 * This file is part of URingPaxos.
 *
 * URingPaxos 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.
 *
 * URingPaxos 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 URingPaxos.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.log4j.Logger;

import ch.usi.da.smr.PartitionManager;
import ch.usi.da.smr.Replica;

/**
 * Name: DfsRecovery<br>
 * Description: <br>
 * 
 * Assumes a local mounted distributed file system to access
 * local and remote snapshots.
 * 
 * Mountpoint: /prefix
 * 
 * Creation date: Nov 6, 2014<br>
 * $Id$
 * 
 * @author Samuel Benz benz@geoid.ch
 */
public class DfsRecovery implements RecoveryInterface {

	private final static Logger logger = Logger.getLogger(DfsRecovery.class);

	public final int nodeID;

	public final String token;
	
	private final PartitionManager partitions;
	
	private final int max_ring = 20;

	private final String path;
	
	private final String prefix;
	
	public static final String snapshot_file = "snapshot.ser";

	public static final String state_file = "snapshot.state"; // only used to quickly decide if remote snapshot is newer

	public DfsRecovery(int nodeID, String token, String prefix, PartitionManager partitions){
		this.nodeID = nodeID;
		this.token = token;
		this.prefix = prefix;
		this.partitions = partitions;
		this.path = prefix + "/" + token + "/" + nodeID;
		File dir = new File(path);
		dir.mkdirs();
	}
	
	public boolean storeState(Map<Integer,Long> instances, Map<String,byte[]> db){
		try {
			synchronized(db){
				logger.info("Replica start storing state ... ");
				for(Entry<Integer,Long> e : instances.entrySet()){
					db.put("r:" + e.getKey(),e.getValue().toString().getBytes());
				}
				FileOutputStream fs = new FileOutputStream(path + "/" + snapshot_file);
		        ObjectOutputStream os = new ObjectOutputStream(fs);
		        os.writeObject(db);
		        os.flush();
		        fs.getChannel().force(false); // fsync
		        os.close();
				fs = new FileOutputStream(path + "/" + state_file);
				for(Entry<Integer, Long> e : instances.entrySet()){
					fs.write((e.getKey() + "=" + e.getValue() + "\n").getBytes());
				}
				fs.getChannel().force(false);
		        os.close();
		        logger.info("... state stored up to instance: " + instances);
			}
	        return true;
		} catch (IOException e) {
			logger.error(e);
		}
		return false;
	}
	
	public Map<Integer,Long> installState(String token,Map<String,byte[]> db) throws Exception{
		Map<Integer,Long> instances = new HashMap<Integer,Long>();
		String node = null;
		InputStreamReader isr;
		String line;
		Map<Integer, Long> state = new HashMap<Integer, Long>();	
		// local
		try{
			isr = new InputStreamReader(new FileInputStream(path + "/" + state_file));
			BufferedReader bin = new BufferedReader(isr);	
			while ((line = bin.readLine()) != null){
				String[] s = line.split("=");
				state.put(Integer.parseInt(s[0]),Long.parseLong(s[1]));
			}
			logger.info("Replica found local snapshot: " + state);
			bin.close();
		}catch (FileNotFoundException e){
			logger.info("No local snapshot present.");
		}
		
		// remote TODO: must ask min. GSQ replicas
		for(String n : partitions.getReplicaIDs(token)){
			try{
				isr = new InputStreamReader(new FileInputStream(prefix + "/" + token + "/" + n + "/" + state_file));
				BufferedReader in = new BufferedReader(isr);
				Map<Integer, Long> nstate = new HashMap<Integer, Long>();
				while ((line = in.readLine()) != null){
					String[] s = line.split("=");
					nstate.put(Integer.parseInt(s[0]),Long.parseLong(s[1]));
				}
				if(state.isEmpty()){
					state = nstate;
					node = n;
				}else if(Replica.newerState(nstate,state)){
					state = nstate;
					node = n;
				}
				logger.info("Replica found remote snapshot: " + nstate + " (" + n + ")");
				in.close();
			}catch(Exception e){
				logger.error("Error getting state from " + n,e);
			}
		}
		synchronized(db){
			InputStream in;
			if(node != null){
				logger.info("Use remote snapshot from host " + node);
				in = new FileInputStream(new File(prefix + "/" + token + "/" + node + "/" + snapshot_file));
			}else{
				in = new FileInputStream(new File(path + "/" + snapshot_file));
			}
			ObjectInputStream ois = new ObjectInputStream(in);
			@SuppressWarnings("unchecked")
			Map<String,byte[]> m = (Map<String,byte[]>) ois.readObject();
			ois.close();
			in.close();
			db.clear();
			db.putAll(m);
			byte[] b = null;
			for(int i = 1;i<max_ring+1;i++){
				if((b = db.get("r:" + i)) != null){
					instances.put(i,Long.valueOf(new String(b)));
				}
			}
		}
		logger.info("Replica installed snapshot instance: " + instances);
		return instances;
	}

	public void close(){

	}

}
