/*******************************************************************************
* Copyright (c) 2013 Imperial College London.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Raul Castro Fernandez - initial design and implementation
******************************************************************************/
package uk.ac.imperial.lsds.seep.reliable;
import java.io.File;
import java.io.IOException;
import java.net.BindException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.ac.imperial.lsds.seep.runtimeengine.CoreRE;
public class BackupHandler implements Runnable{
final private Logger LOG = LoggerFactory.getLogger(BackupHandler.class);
//The core that owns this control handler
private CoreRE owner;
//The connection port that this backupHandler must use
private int connPort;
//This variable controls if this Runnable should keep running or not
private boolean goOn;
//Variable to control if a session is running or not
private AtomicBoolean isSessionClosed = new AtomicBoolean(true);
// Session related variables
private String sessionName = null;
private int transNumber = -1;
private String lastSessionName = null;
private long s_sessiontime = 0;
private HashMap<InetAddress, BackupSessionInfo> openSessions = new HashMap<InetAddress, BackupSessionInfo>();
private HashMap<Integer, ArrayList<FileChannel>> sessionHandlers = new HashMap<Integer, ArrayList<FileChannel>>();
// To access rw files with backups
private HashMap<Integer, ArrayList<FileChannel>> backupSessionHandlers = new HashMap<Integer, ArrayList<FileChannel>>();
// To access the same files as above, with the possibility of removing them from the OS file system
private HashMap<Integer, ArrayList<File>> sessionHandlersGCFiles = new HashMap<Integer, ArrayList<File>>();
private HashMap<Integer, ArrayList<File>> backupSessionHandlersGCFiles = new HashMap<Integer, ArrayList<File>>();
private HashMap<Integer, String> lastSessionNames = new HashMap<Integer, String>();
// //Variables to keep the backup handler
// private ArrayList<MappedByteBuffer> lastBackupHandlers = new ArrayList<MappedByteBuffer>();
// private ArrayList<MappedByteBuffer> backupLastBackupHandlers = new ArrayList<MappedByteBuffer>();
public CoreRE getOwner(){
return owner;
}
public void setOwner(CoreRE owner){
this.owner = owner;
}
public boolean getGoOn(){
return goOn;
}
public void setGoOn(boolean goOn){
this.goOn = goOn;
}
public String getLastBackupSessionName(int opId){
return lastSessionNames.get(opId);
}
public BackupHandler(CoreRE owner, int port) {
this.owner = owner;
this.connPort = port;
this.goOn = true;
File newFile = new File("backup/");
newFile.mkdirs();
}
public void openSession(int opId, InetAddress remoteAddress){
LOG.debug("New Backup session opened for OP: {}", opId);
s_sessiontime = System.currentTimeMillis();
// We name the new session
sessionName = new Long(System.currentTimeMillis()).toString();
transNumber = -1;
ArrayList<FileChannel> lastBackupHandlers = new ArrayList<FileChannel>();
ArrayList<File> lastBackupHandlersGCFiles = new ArrayList<File>();
// We backup the previous handlers if there are any, keeping association with the operator establishing the connection
if(sessionHandlers.containsKey(opId)){
backupSessionHandlers.put(opId, sessionHandlers.get(opId));
backupSessionHandlersGCFiles.put(opId, sessionHandlersGCFiles.get(opId));
}
// And we put the new ones here
sessionHandlers.put(opId, lastBackupHandlers);
sessionHandlersGCFiles.put(opId, lastBackupHandlersGCFiles);
BackupSessionInfo bsi = new BackupSessionInfo(opId, lastBackupHandlers, this, sessionName, transNumber);
// We log the open session, identifying it with the IP
openSessions.put(remoteAddress, bsi);
System.out.println("NEW SESSION: "+remoteAddress.toString());
}
public void closeSession(int opId, InetAddress remoteAddress){
lastSessionNames.put(opId, openSessions.get(remoteAddress).getSessionName());
openSessions.remove(remoteAddress);
System.out.println("TOTAL SESSION TIME: "+(System.currentTimeMillis() - s_sessiontime));
// If the session went well, then we get rid of the old files
if(backupSessionHandlers.containsKey(opId)){
ArrayList<FileChannel> oldFiles = backupSessionHandlers.get(opId);
for(FileChannel f : oldFiles){
try {
f.close();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
ArrayList<File> toRemove = backupSessionHandlersGCFiles.get(opId);
for(File f : toRemove){
f.delete();
}
}
}
public void addBackupHandler(int opId, FileChannel fc, File f){
sessionHandlers.get(opId).add(fc);
sessionHandlersGCFiles.get(opId).add(f);
}
public ArrayList<File> getSessionFileHandlers(int opId){
return backupSessionHandlersGCFiles.get(opId);
}
@Override
public void run() {
ServerSocket backupServerSocket = null;
try{
//Establish listening port
backupServerSocket = new ServerSocket(connPort);
LOG.info("-> BackupHandler listens on port: {} for connections", connPort);
//while goOn is active
while(goOn){
Socket incomingConn = backupServerSocket.accept();
InetAddress incomingAddr = incomingConn.getInetAddress();
// If session was previously opened
if(openSessions.containsKey(incomingAddr)){
BackupSessionInfo bsi = openSessions.get(incomingAddr);
bsi.incrementTransNumber();
// With an opened session we wait for connections and pass the sessionName and the transmission number
BackupHandlerWorker bhw = new BackupHandlerWorker(bsi.getOpId(), incomingConn, this, bsi.getSessionName(), bsi.getTransNumber());
///\todo{Reduce the overhead of the thread creation at this point. Use a pool or reuse the same worker}
Thread newConn = new Thread(bhw);
newConn.start();
}
else{
LOG.warn("Sent backup chunk from OPID. SESSION CLOSED HERE: "+incomingAddr);
}
}
backupServerSocket.close();
}
catch(BindException be){
LOG.error("-> BIND EXC IO Error "+be.getMessage());
LOG.error("-> backupServerSocket.toString: "+backupServerSocket.toString());
be.printStackTrace();
}
catch(IOException io){
LOG.error("-> BackupHandler. While listening incoming conns "+io.getMessage());
io.printStackTrace();
}
}
}