/* * Created on 01.03.2005 */ package jplagWebService.serverImpl; import java.io.File; import java.util.Date; import jplag.Program; import jplag.options.CommandLineOptions; import jplag.options.util.ZipUtil; import jplagWebService.server.JPlagException; import jplagWebService.server.UserInfo; import jplagWebService.serverAccess.AccessStructure; import jplagWebService.serverAccess.MailTemplateAdmin; import jplagWebService.serverAccess.MemoryManager; import jplagWebService.serverAccess.ResultAdmin; import jplagWebService.serverAccess.StatusDecorator; import jplagWebService.serverAccess.TransferManager; import jplagWebService.serverAccess.UserAdmin; /** * @author Moritz Kroll, Emeric Kwemou */ public class JPlagCentral extends Thread { /** * The JPlagCentral singleton object */ private static JPlagCentral JPLAG_CENTRAL = null; /* * Some constants */ public static final int READYQUEUE = ResultAdmin.SEARCH_ENTRIES; public static final int TERMINATEDQUEUE = ResultAdmin.SEARCH_RESULTS; public static final int ALLQUEUES = ResultAdmin.SEARCH_ALL; private static final int MAX_SUBMISSIONS_ON_SERVER=5; /** * The currently processed submission structure */ protected static AccessStructure activeStruct = null; /** * If set to true forces the main thread to be ended */ private static boolean doStopCentral = false; /** * A singleton ResultAdministration object */ private static ResultAdmin resultAdmin = null; /** * A singleton UserAdmin object */ private static UserAdmin userAdmin = null; /** * A singleton MailTemplateAdmin object */ private static MailTemplateAdmin mailTemplateAdmin = null; /** * A singleton TransferManager object */ private static TransferManager transferManager = null; private MemoryManager memoryManager; /** * The private constructor to be used to construct the singleton object. * Sets up the MemoryManager and ResultAdministration objects */ private JPlagCentral() { memoryManager = MemoryManager.giveInstance(); memoryManager.setManager_enabled(true); resultAdmin = new ResultAdmin(AccessStructure.getJPLAG_DIRECTORY()); userAdmin = new UserAdmin(AccessStructure.getJPLAG_DIRECTORY()); mailTemplateAdmin = new MailTemplateAdmin( AccessStructure.getJPLAG_DIRECTORY()); transferManager = new TransferManager(); } /** * @return A singleton object of JPlagCentral */ public static JPlagCentral getInstance() { if (JPLAG_CENTRAL == null) JPLAG_CENTRAL = new JPlagCentral(); return JPLAG_CENTRAL; } public UserAdmin getUserAdmin() { return userAdmin; } public MailTemplateAdmin getMailTemplateAdmin() { return mailTemplateAdmin; } public TransferManager getTransferManager() { return transferManager; } public static synchronized AccessStructure search(int select, String submissionID) { if (activeStruct != null && activeStruct.getSubmissionID().compareTo(submissionID) == 0) { activeStruct.getDecorator().setState( activeStruct.getOption().getState()); activeStruct.getDecorator().setProgress( activeStruct.getOption().getProgress()); return activeStruct; } return resultAdmin.getAccessStruct(submissionID, select); } public static synchronized void addToReadyQueue(AccessStructure struct) { struct.getDecorator().setState(StatusDecorator.WAITING_IN_QUEUE); struct.getDecorator().setProgress(resultAdmin.getNumEntries()); resultAdmin.addEntry(struct); } // TODO: rename this function! public static synchronized void addToTerminatedQueue(AccessStructure struct) { if(struct==activeStruct) activeStruct=null; } public static synchronized void addToTerminatedQueue(AccessStructure struct, int state,int progress,String message) { struct.getDecorator().add(state,progress,message); if(struct==activeStruct) activeStruct=null; } private static synchronized AccessStructure getNextReadyEntry() { activeStruct = resultAdmin.getNextEntry(); return activeStruct; } /** * @return If the submissionID belongs to the activeStruct it returns * its state, otherwise it returns 0 */ public static synchronized int getStateIfActive(String user,String subID) { if(activeStruct!=null && activeStruct.getSubmissionID().equals(subID) && activeStruct.getUsername().equals(user)) return activeStruct.getOption().getState(); return 0; } /** * Stops the JPlagCentral by setting some stop flags */ public synchronized void stopCentral() { doStopCentral = true; if(activeStruct!=null) activeStruct.getOption().forceProgramToStop(); if(memoryManager==null) { // XXX: Sometimes stopCentral() is called several times?? System.out.println("Memory manager has already been deleted!?"); return; } memoryManager.setManager_enabled(false); userAdmin.stopUserAdmin(); transferManager.stopTransferManager(); // give threads time to complete their current work try { Thread.sleep(1000); } catch(Exception e) {} // if still alive, interrupt any sleep loops if(memoryManager.isAlive()) memoryManager.interrupt(); if(userAdmin.isAlive()) userAdmin.interrupt(); if(transferManager.isAlive()) transferManager.interrupt(); memoryManager = null; userAdmin = null; transferManager = null; } /** * Main loop processing the submissions */ public void run() { memoryManager.start(); userAdmin.start(); transferManager.start(); System.out.println("[" + new Date() + "] JPlagCentral started"); try { Program program = null; while (!doStopCentral) { AccessStructure struct = getNextReadyEntry(); if (struct != null) { CommandLineOptions opt = struct.getOption(); try { System.out.println("[" + new Date() + "] New project " + "found: " + opt.root_dir); struct.unzipEntry(); program = new Program(opt); program.run(); File result = program.get_jplagResult(); ZipUtil.zip(result, result.getParentFile().getPath()); System.out.println("[" + new Date() + "] Project done, " + "saved to " + result.getParentFile().getPath()); addToTerminatedQueue(struct, StatusDecorator.COMPARE_SOURCE_DONE, 100, ""); struct.setDate(System.currentTimeMillis()); resultAdmin.addResult(struct); program.closeWriter(); program = null; System.gc(); try { AccessStructure.deleteDir(result); } catch (SecurityException ex) { System.out.println("Not allowed to delete results!"); } struct.deleteEntryFiles(); } catch (OutOfMemoryError e) { // Do panic garbage collection (doing anything else // would cause another OutOfMemoryError) program = null; System.gc(); System.out.println("OutOfMemoryError!!!"); e.printStackTrace(); // though there's nothing anyway struct.getDecorator().add(jplag.ExitException.UNKNOWN_ERROR_OCCURRED, -1, "Project terminated because of an OutOfMemoryError!"); addToTerminatedQueue(struct); struct.setDate(System.currentTimeMillis()); resultAdmin.addResult(struct); } catch (Throwable e) { if (e instanceof jplag.ExitException) { jplag.ExitException ex = (jplag.ExitException) e; System.out.println("[" + new Date() + "] " + "ExitException occurred: State=" + ex.getState() + " Report=" + ex.getReport()); struct.getDecorator().add(ex); } else if (struct.getOption().isForceStop()) { struct.getDecorator().add(jplag.options.Options.SUBMISSION_ABORTED, -1, "Submission aborted!"); System.out.println("Force stop exception occurred!"); e.printStackTrace(); } else { struct.getDecorator().add(jplag.ExitException.UNKNOWN_ERROR_OCCURRED, -1, "Project terminated with errors: " + e.getMessage()); System.out.println("Exception occurred!"); e.printStackTrace(); } addToTerminatedQueue(struct); /* * If exception was thrown because of an error, move * this submission into the result pool. Otherwise when * the server is going to be stopped the submission * should stay in the entry queue to be processed the * next time the server starts */ if (!struct.getOption().isForceStop()) { struct.setDate(System.currentTimeMillis()); resultAdmin.addResult(struct); } if (program != null) { program.closeWriter(); program = null; } System.gc(); } } else { try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } } } } catch (Throwable t) { System.out.println("Unknown exception caught!!! " + t.getMessage()); t.printStackTrace(); if (activeStruct != null) { try { activeStruct.getDecorator().add(jplag.ExitException.UNKNOWN_ERROR_OCCURRED, -1, "Unknown exception caught!!! " + t.getMessage()); } catch (Throwable t2) { System.out.println("Setting activeStruct's error message " + "caused another exception! " + t2.getMessage()); t2.printStackTrace(); } } } System.out.println("[" + new Date() + "] JPlagCentral stopped"); } /** * @param struct Submission to be canceled (may be in entry queue, in the * processing or an result) * @return True, if everything went fine */ public static boolean cancelSubmission(AccessStructure struct){ if(struct==activeStruct) activeStruct.getOption().forceProgramToStop(); resultAdmin.deleteSubmission(struct); return struct.delete(); } protected static jplagWebService.server.Submission[] listSubmissions(String username){ return resultAdmin.listSubmissions(username); } public static AccessStructure[] listAccessStructures(String username){ return resultAdmin.listAccessStructures(username); } public static String getNextSubmissionID(){ return resultAdmin.getNextSubmissionID(); } public static String[] usersList(){ return resultAdmin.usersList(); } public static void checkQuota(String username) throws JPlagException { int numentries=resultAdmin.getNumUserEntries(username); int numresults=resultAdmin.getNumUserResults(username); if(numentries>=MAX_SUBMISSIONS_ON_SERVER) { throw new JPlagException("quotaException", "Too many waiting submissions ("+numentries+")!", "Please wait for a submission to be finished or cancel one."); } if(numresults>=MAX_SUBMISSIONS_ON_SERVER) { throw new JPlagException("quotaException", "Too many finished submissions ("+numresults+")!", "Please download and delete a finished submission."); } if(numentries+numresults>=MAX_SUBMISSIONS_ON_SERVER) { throw new JPlagException("quotaException", "Too many waiting and finished submissions ("+numentries+"+" + numresults+")!","Please download and delete a finished " + "submission, wait for a submission to be finished or cancel " + "a waiting submission."); } } public static UserInfo getUserInfo(String username) { int numentries=resultAdmin.getNumUserEntries(username); int numresults=resultAdmin.getNumUserResults(username); int leftSlots=MAX_SUBMISSIONS_ON_SERVER-numentries-numresults; return userAdmin.getUserInfo(username,leftSlots); } }