package server.naming; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.Timer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import rmi.NamingService; import rmi.NamingServiceImpl; import rmi.StorageService; import server.Machine; import server.Server; import util.Variables; import datastructure.FileUnit; public class NamingServer implements Server { private static final Logger logger = LogManager.getLogger(NamingServer.class); private static NamingServer INSTANCE = null; public static NamingServer getInstance() { if ( INSTANCE == null ) INSTANCE = new NamingServer(); return INSTANCE; } private NamingServer() { me = new Machine("namingServer"); root = new FileUnit("root", true); } public final Map<Machine, Long> storageValids = new HashMap<>(); public final Machine me; private StorageService storageService; private boolean connectToStorageServer(Machine machine) { try { storageService = (StorageService) Naming.lookup(machine.getAddress(StorageService.class.getName())); return true; } catch (Exception e) { logger.error(e); e.printStackTrace(); } return false; } /** * record the virtual user-view document tree structure * the root directory */ private final FileUnit root; public FileUnit getRoot() { return root; } @Override public void loadService() { try { final NamingServiceImpl service = new NamingServiceImpl(); LocateRegistry.createRegistry(me.port); Naming.rebind(me.getAddress(NamingService.class.getName()), service); } catch (Exception e) { logger.error(e); e.printStackTrace(); } } /** * Check whether each storage server is valid now. <br/> * If not valid, remove the storage server. */ public void checkStorages() { final long now = new Date().getTime(); final long threshold = Long.parseLong(Variables.getInstance().getProperty("contactThreshold")); Set<Machine> toRemove = new HashSet<>(); for (Machine machine : storageValids.keySet()) { final long date = storageValids.get(machine); if ( now - date > threshold ) { toRemove.add(machine); } } for (Machine machine : toRemove) { logger.info(String.format("removing %s (last contact: %s)", machine, new Date(storageValids.get(machine)))); refreshDirRecordWithMachineInvalid(machine, root); storageValids.remove(machine); } } /** * First, immigrate toCopy set. * Then, check if there are at least 3 copies for each file, * and put those files to toCopy set. */ @Deprecated public void checkCopies() { // TODO } private void refreshDirRecordWithMachineInvalid(Machine invalidMachine, FileUnit nowDir) { List<FileUnit> fileUnits = nowDir.list(); for (int j = 0; j < fileUnits.size(); j++) { FileUnit fileUnit = fileUnits.get(j); if (fileUnit.isDir()) { refreshDirRecordWithMachineInvalid(invalidMachine, fileUnit); } fileUnit.deleteStorageMachine(invalidMachine); if (fileUnit.getAllMachines().size() == 0) { nowDir.deleteLowerFileUnit(fileUnit); j--; } } } /** * Set a timer to check whether each storage server is available * @return timer */ public Timer startValidTimer() { final int interval = Integer.parseInt(Variables.getInstance().getProperty("contactInterval")); final Timer timer = new Timer(interval, new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { try { NamingServer.getInstance().checkStorages(); } catch (Exception e) { logger.error(e); e.printStackTrace(); } } }); timer.start(); return timer; } /** * Set a timer to check if there are at least 3 copies of each file */ @Deprecated public Timer startCopyTimer() { final int interval = Integer.parseInt(Variables.getInstance().getProperty("copyThreshold")); final Timer timer = new Timer(interval, new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { try { NamingServer.getInstance().checkCopies(); } catch (Exception e) { logger.error(e); e.printStackTrace(); } } }); timer.start(); return timer; } public void checkDupFileNumAndIncrease(FileUnit nowDir, String nowPath) { List<FileUnit> fileUnits = nowDir.list(); for (int j = 0; j < fileUnits.size(); j++) { FileUnit fileUnit = fileUnits.get(j); int dupTarget = 3; if (storageValids.size() < 3) { dupTarget = storageValids.size(); } if (fileUnit.getAllMachines().size() < dupTarget) { Machine storedMachine = fileUnit.getAllMachines().get(0); String fullDirPath = nowPath + fileUnit.getName(); int storeIndex = fullDirPath.hashCode() % storageValids.size(); storeIndex = (storeIndex + storageValids.size()) % storageValids.size(); int backNum = dupTarget - fileUnit.getAllMachines().size(); while (backNum > 0) { Machine machine = new ArrayList<>(storageValids.keySet()).get(storeIndex); storeIndex = (storeIndex + 1) % storageValids.size(); if (!fileUnit.isStoredAtMachine(machine)) { connectToStorageServer(machine); if (fileUnit.isDir()) { try { storageService.createDir(fullDirPath, false); } catch (RemoteException e) { e.printStackTrace(); } } else { try { storageService.createFile(fullDirPath, false); connectToStorageServer(storedMachine); byte[] data = storageService.getFile(fullDirPath); connectToStorageServer(machine); storageService.appendWriteFile(fullDirPath, data, false); } catch (RemoteException e) { e.printStackTrace(); } } backNum--; } } } if (fileUnit.isDir()) { checkDupFileNumAndIncrease(fileUnit, nowPath + fileUnit.getName() + "/"); } } } public static void main(String[] args) { NamingServer.getInstance().loadService(); NamingServer.getInstance().startValidTimer(); // NamingServer.getInstance().startCopyTimer(); } }