package jplagWebService.serverAccess; /** * Author Emeric Kwemou, Moritz Kroll */ //import java.util.Date; import java.io.File; import java.io.FileOutputStream; import java.util.Vector; import javax.activation.DataHandler; import javax.activation.FileDataSource; import javax.activation.MimetypesFileTypeMap; import javax.mail.MessagingException; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMultipart; import jplag.options.CommandLineOptions; import jplag.options.util.ZipUtil; import jplagWebService.server.JPlagException; import jplagWebService.server.Status; import jplagWebService.serverImpl.JPlagCentral; import jplagWebService.serverImpl.JPlagTypImpl; import org.w3c.dom.Document; import org.w3c.dom.Element; public class AccessStructure { public static final int MAX_UNZIPPED_SIZE = 60 * 1024 * 1024; /* * Directory settings */ private static String JPLAG_DIRECTORY = null; private static String JPLAG_ENTRIES_DIRECTORY = null; private static String JPLAG_RESULTS_DIRECTORY = null; /* * Some defaults */ private static final int DEFAULT_STORABLE = 20; private static final int MAX_STORABLE = 50; private static final int MIN_STORABLE = 10; private static final String PARSERLOG = "parser-log.txt"; /* * Class attributes */ private String title = null; private String username = null; private String submissionID = "Not initialised!"; private long date = 0; private String commandLineInString = ""; private long lastStatusRequest = 0; // System.currentTimeMillis(); private jplag.options.CommandLineOptions options = null; private StatusDecorator dec; /** * Constructs a new AccessStructure object for an Option */ public AccessStructure(String user, jplagWebService.server.Option usr_option) throws JPlagException { dec = new StatusDecorator(new Status()); submissionID = JPlagCentral.getNextSubmissionID(); commandLineInString = generateCMD(usr_option); username = user; title = usr_option.getTitle(); date = System.currentTimeMillis(); generateStructure(usr_option, commandLineInString); } /** * Constructs a new AccessStructure object for an Option and a MimeMultipart */ public AccessStructure(String user, jplagWebService.server.Option usr_option, MimeMultipart inputZipFile) throws JPlagException { this(user, usr_option); saveZipFile(inputZipFile); } /** * Protected constructor to be used to recreate an AccessStructure out of an * XML tag */ protected AccessStructure(String submissionid, CommandLineOptions copts, String username, String title, long date, int laststate, String report) { dec = new StatusDecorator(new Status(laststate, 0, report)); this.username = username; this.title = title; this.submissionID = submissionid; this.date = date; options = copts; } /** * Ensure that temporary directories for entries and results exist */ public static void ensureExistence() { JPLAG_DIRECTORY = System.getProperty("jplag_home"); if (JPLAG_DIRECTORY == null) { System.out.println("jplag_home property not set!!"); return; } System.out.println("jplag_home = " + JPLAG_DIRECTORY); JPLAG_ENTRIES_DIRECTORY = JPLAG_DIRECTORY + File.separator + "entries"; JPLAG_RESULTS_DIRECTORY = JPLAG_DIRECTORY + File.separator + "results"; File f = new File(JPLAG_ENTRIES_DIRECTORY); f.mkdir(); f = new File(JPLAG_RESULTS_DIRECTORY); f.mkdir(); } /** * @return Command line options to be displayed in the result file */ private static String generateCMD(jplagWebService.server.Option opt) { String result = "path_to_files=" + opt.getPathToFiles() + " min_token_length=" + opt.getMinimumMatchLength() + " basecode_dir=" + opt.getBasecodeDir() + " read_subdirs=" + opt.isReadSubdirs() + " clustertype=" + opt.getClustertype() + " store_matches=" + opt.getStoreMatches() + " suffixes="; for(int i=0; i<opt.getSuffixes().length; i++) { result += opt.getSuffixes()[i] + ((i==opt.getSuffixes().length-1) ? "" : ","); } result += " language=" + opt.getLanguage() + " title=" + opt.getTitle(); return result; } /** * @return True, if the given string is not a null or empty string. * False, otherwise */ private static boolean isSet(String str) { return str!=null && str.length()!=0; } /** * This method is used to translate a jplag.server.Option object to a * jplag.options.CommandLineOptions object * * @param usr_opt * @param argsInString * @throws JPlagException, if the options are not valid */ private void generateStructure(jplagWebService.server.Option usr_opt, String argsInString) throws JPlagException { Vector<String> vec = new Vector<String>(); vec.add("JPlag"); // Verbose parser and verbose quiet (don't fill server logs) vec.add("-vp"); vec.add("-vq"); if (usr_opt.isReadSubdirs()) vec.add("-s"); if (isSet(usr_opt.getPathToFiles())) { // Search with pattern <rootdir>/*/<pathToFiles> vec.add("-S"); vec.add(usr_opt.getPathToFiles()); } vec.add("-m"); String tmpmatch = usr_opt.getStoreMatches(); if (!isSet(tmpmatch)) { vec.add(DEFAULT_STORABLE + ""); } else { int index = tmpmatch.indexOf("%"); try { if (index != -1) { tmpmatch = tmpmatch.substring(0, index); int percent = Integer.parseInt(tmpmatch); if (percent > 100) { throw new JPlagException("optionsException", "Illegal store matches option specified!", "There can't be a similarity bigger than 100%!"); } tmpmatch += "%"; } else { int nummatches = Integer.parseInt(tmpmatch); if (nummatches > MAX_STORABLE) nummatches = MAX_STORABLE; if (nummatches < MIN_STORABLE) nummatches = MIN_STORABLE; tmpmatch = nummatches + ""; } } catch (NumberFormatException ex) { throw new JPlagException("optionsException", "Illegal store matches option specified!", "Please check the usage function for correct " + "store_matches format"); } vec.add(tmpmatch); } // Comparison mode vec.add("-compmode"); if(usr_opt.getComparisonMode() == null) vec.add(jplag.options.Options.COMPMODE_NORMAL + ""); else vec.add(usr_opt.getComparisonMode().toString()); // Language the output should "speak" vec.add("-clang"); vec.add(usr_opt.getCountryLang()); // Suffixes if (usr_opt.getSuffixes().length != 0) { vec.add("-p"); String str = ""; String[] suffixes = usr_opt.getSuffixes(); for(int i=0; i<suffixes.length; i++) { str += suffixes[i]; if(i != suffixes.length-1) str += ","; } vec.add(str); } // Use base code directory? if (isSet(usr_opt.getBasecodeDir())) { vec.add("-bc"); vec.add(usr_opt.getBasecodeDir()); } // Clustering if (isSet(usr_opt.getClustertype())) { vec.add("-clustertype"); vec.add(usr_opt.getClustertype()); } // Language String language = usr_opt.getLanguage(); int languagenum; // Check whether this language name exists for (languagenum = 0; languagenum < JPlagTypImpl.languageInfos.length; languagenum++) { if (JPlagTypImpl.languageInfos[languagenum].getName().equals(language)) { vec.add("-l"); vec.add(language); break; } } if (languagenum == JPlagTypImpl.languageInfos.length) { System.out.println("Wrong language specified: " + language); throw new JPlagException( "optionsException", "Illegal language specified!", "Use getServerInfo to see which languages are supported " + "and how they are spelled!"); } // Detecting sensitivity // TODO: Should there be a minimal minimum match length being accepted?? int mintoklen = usr_opt.getMinimumMatchLength(); if (mintoklen <= 0) mintoklen = JPlagTypImpl.languageInfos[languagenum].getDefMinMatchLen(); vec.add("-t"); vec.add(mintoklen + ""); // Result dir String submissiondir = submissionID + username; String result_dir = JPLAG_RESULTS_DIRECTORY + File.separator + submissiondir; vec.add("-r"); vec.add(result_dir); // Output dir vec.add("-o"); File file12s = new File(result_dir); if (!(file12s.exists())) file12s.mkdir(); String tmp11st = result_dir + File.separator + PARSERLOG; vec.add(tmp11st); // Original dir vec.add("-d"); vec.add(usr_opt.getOriginalDir()); // Title vec.add("-title"); vec.add(usr_opt.getTitle()); // Root_dir vec.add(JPLAG_ENTRIES_DIRECTORY + File.separator + submissiondir); /** * All command line options have now been set and a first option * validation has been done */ try { String[] args = new String[vec.size()]; for (int i = 0; i < args.length; i++) args[i] = vec.elementAt(i); options = new jplag.options.CommandLineOptions(args, argsInString); getDecorator().add(options.getState(), options.getProgress(), ""); } catch (jplag.ExitException e) { throw new JPlagException("invalidOptionsException", e.getReport(), "Check your options"); } } /** * @return The path (including the filename) to the result zip file */ public String getEntryPath() { return JPLAG_ENTRIES_DIRECTORY + File.separator + getSubmissionID() + getUsername() + ".zip"; } /** * Saves the zip file inside the entries directory */ public void saveZipFile(MimeMultipart inputZipFile) { try { MimeBodyPart bdp = (MimeBodyPart) inputZipFile.getBodyPart(0); DataHandler dh = bdp.getDataHandler(); File part = new File(getEntryPath()); FileOutputStream fos = new FileOutputStream(part); dh.writeTo(fos); fos.close(); System.gc(); } catch (Exception e) { e.printStackTrace(); } } /** * Deletes a whole directory including subdirectories * * XXX: Recursive routine! Beware stack overflows!? */ public static void deleteDir(File dir) { if (!dir.isDirectory()) { System.out.println("Util::deleteDir(): " + dir + " is not a directory!"); return; } File[] files = dir.listFiles(); for (int i = 0; i < files.length; i++) { if (files[i].isDirectory()) deleteDir(files[i]); else files[i].delete(); } dir.delete(); } /** * Deletes the submission input zip file and the unpacked contents * * @return True, if server allowed to delete the files */ public boolean deleteEntryFiles() { String tmp2 = getSubmissionID() + getUsername(); File zipfile = new File(JPLAG_ENTRIES_DIRECTORY + File.separator + tmp2 + ".zip"); File entrydir = new File(JPLAG_ENTRIES_DIRECTORY + File.separator + tmp2); try { zipfile.delete(); if(entrydir.exists()) deleteDir(entrydir); } catch (SecurityException ex) { System.out.println("Not allowed to delete entry files!"); return false; } return true; } /** * Deletes the submission result zip file * * @return True, if server allowed to delete the files */ public boolean deleteResultFiles() { /* * if (!resultAdmin.deleteResult(struct.getUsername(), struct * .getSubmissionID())) return false; */ String tmp2 = getSubmissionID() + getUsername(); File zipfile = new File(JPLAG_RESULTS_DIRECTORY + File.separator + tmp2 + ".zip"); File resultdir = new File(JPLAG_RESULTS_DIRECTORY + File.separator + tmp2); try { zipfile.delete(); if (resultdir.exists()) deleteDir(resultdir); } catch (SecurityException ex) { System.out.println("Not allowed to delete zipped result file!"); return false; } return true; } /** * Deletes all files from this submission * * @return True, if server allowed to delete the files */ public boolean delete() { if (getState() < 300) return deleteEntryFiles(); else return deleteResultFiles(); } /** * @return A MimeMultipart object containing the zipped result files */ public MimeMultipart getResult() { File file = new File(JPLAG_RESULTS_DIRECTORY + File.separator + submissionID + getUsername() + ".zip"); MimeMultipart mmp = new MimeMultipart(); FileDataSource fds1 = new FileDataSource(file); MimetypesFileTypeMap mftp = new MimetypesFileTypeMap(); mftp.addMimeTypes("multipart/zip zip ZIP"); fds1.setFileTypeMap(mftp); MimeBodyPart mbp = new MimeBodyPart(); try { mbp.setDataHandler(new DataHandler(fds1)); mbp.setFileName(file.getName()); mmp.addBodyPart(mbp); } catch (MessagingException me) { me.printStackTrace(); } return mmp; } /** * @return The path (including the filename) to the result zip file */ public String getResultPath() { return JPLAG_RESULTS_DIRECTORY + File.separator + getSubmissionID() + getUsername() + ".zip"; } /** * Unzips the submission input file into a <submissionID><username>folder * inside the entries directory */ public void unzipEntry() throws jplag.ExitException { String tmp2 = getSubmissionID() + getUsername(); File part = new File(JPLAG_ENTRIES_DIRECTORY + File.separator + tmp2 + ".zip"); int totalsize = ZipUtil.unzip(part, JPLAG_ENTRIES_DIRECTORY, tmp2); if(totalsize > MAX_UNZIPPED_SIZE) throw new jplag.ExitException("Submission too big! It may be " + (MAX_UNZIPPED_SIZE / 1024) + " kB at maximum, but it is " + (totalsize / 1024) + " kB!"); File unzipped = new File(JPLAG_ENTRIES_DIRECTORY + File.separator + tmp2); // Searching root dir File[] files = unzipped.listFiles(); String root_dir = (files.length == 1) ? files[0].getPath() : unzipped.getPath(); files = null; getOption().root_dir = root_dir; System.gc(); } public static String getJPLAG_DIRECTORY() { return JPLAG_DIRECTORY; } public StatusDecorator getDecorator() { return this.dec; } /** * Simpler version of setLastStatusRequest(long lastStatusRequest) The * parameter is always System.currentTimeMillis(); */ public void setLastStatusRequest() { setLastStatusRequest(System.currentTimeMillis()); } /** * @param lastStatusRequest: * The last time user has performed a getStatus request. This * method is used to prohibit getStatus flooding */ public synchronized void setLastStatusRequest(long lastStatusRequest) { this.lastStatusRequest = lastStatusRequest; } /** * @return Last time of status request */ public long getLastStatusRequest() { return lastStatusRequest; } public int getState() { return dec.getState(); } public jplag.options.CommandLineOptions getOption() { return this.options; } public String getTitle() { return title; } public String getUsername() { return username; } public String getSubmissionID() { return submissionID; } public void setSubmissionID(String subid) { submissionID = subid; } public long getDate() { return date; } public void setDate(long date) { this.date = date; } public String getCMDInString() { return this.commandLineInString; } /** * @param doc * The document for which the new element is to be created * @return An "entry" element to be stored in the user submission database */ public Element toXMLEntryElement(Document doc) { Element entry = doc.createElement("entry"); entry.setAttribute("id", getSubmissionID()); entry.setAttribute("username", getUsername()); entry.setAttribute("title", getTitle()); entry.setAttribute("date", Long.toString(getDate())); entry.setAttribute("commandLine", getCMDInString()); jplag.options.CommandLineOptions copt = getOption(); String[] args = copt.getArgs(); entry.setAttribute("numargs", args.length + ""); for (int i = 0; i < args.length; i++) { entry.setAttribute("arg_" + i, args[i]); } return entry; } /** * @param doc * The document for which the new element is to be created * @return A "result" element to be stored in the user submission database */ public Element toXMLResultElement(Document doc) { Element submission = doc.createElement("result"); submission.setAttribute("id", getSubmissionID()); submission.setAttribute("title", getTitle()); submission.setAttribute("date", Long.toString(date)); submission.setAttribute("laststate", getState() + ""); if(dec.getReport().length()>0) submission.setAttribute("report", dec.getReport()); return submission; } /** * @param entry * Entry element to be parsed * @return AccessStructure object being usable in JPlagCentral.run() */ public static AccessStructure fromXMLEntryElement(Element entry) { String subID = entry.getAttribute("id"); String username = entry.getAttribute("username"); String title = entry.getAttribute("title"); long date; try { date = Long.parseLong(entry.getAttribute("date")); } catch (NumberFormatException ex) { System.out.println("jplag.server.serverAccess.AccessStructure:" + " NumberFormatException during date=parseInt(" + entry.getAttribute("date") + ")"); date = 0; } int numargs; try { numargs = Integer.parseInt(entry.getAttribute("numargs")); } catch (NumberFormatException ex) { System.out.println("jplag.server.serverAccess.AccessStructure:" + " NumberFormatException during numargs=parseInt(" + entry.getAttribute("numargs") + ")"); numargs = 0; } String[] args = new String[numargs]; String commandLine = entry.getAttribute("commandLine"); for (int i = 0; i < args.length; i++) { args[i] = entry.getAttribute("arg_" + i); } jplag.options.CommandLineOptions copt; try { copt = new jplag.options.CommandLineOptions(args, commandLine); //Added Emeric Kwemou 22-03-05. I just conserve the commandline in // a string } catch (jplag.ExitException ex) { System.out.println("ExitException caught in getNextEntry()"); ex.printStackTrace(); return null; } return new AccessStructure(subID, copt, username, title, date, StatusDecorator.WAITING_IN_QUEUE, ""); } /** * @param submission * Result element to be parsed * @return AccessStructure object for informational use */ public static AccessStructure fromXMLResultElement(Element submission) { Element user = (Element) submission.getParentNode(); String subID = submission.getAttribute("id"); String username = user.getAttribute("username"); String title = submission.getAttribute("title"); long date; try { date = Long.parseLong(submission.getAttribute("date")); } catch (NumberFormatException ex) { System.out.println("jplag.server.serverAccess.AccessStructure:" + " NumberFormatException during date=parseInt(" + submission.getAttribute("date") + ")"); date = 0; } int laststate; try { laststate = Integer.parseInt(submission.getAttribute("laststate")); } catch (NumberFormatException ex) { System.out.println("jplag.server.serverAccess.AccessStructure:" + " NumberFormatException during laststate=parseInt(" + submission.getAttribute("laststate") + ")"); laststate = 499; } String report = submission.getAttribute("report"); return new AccessStructure(subID, null, username, title, date, laststate, report); } }