2010-01-28 73 views
6

我正在爲Java學習編寫Web應用程序。使用哪些用戶可以在我的服務器上編譯他們的代碼+運行該代碼。 編譯很容易與JavaCompiler進行:使用JavaCompiler和ClassLoader編譯和運行用戶代碼

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); 
    CompilationTask task = compiler.getTask(null, null, diagnostics, null, null, prepareFile(nazwa, content)); 

    task.call(); 

    List<String> returnErrors = new ArrayList<String>(); 
    String tmp = new String(); 
    for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { 
     tmp = String.valueOf(diagnostic.getLineNumber()); 
     tmp += " msg: " + diagnostic.getMessage(null); 
     returnErrors.add(tmp.replaceAll("\n", " ")); 
    } 

我管理加載類代碼:

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
    StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null); 

    try { 
     URL[] urls = {new URL("file:///root/"), new URL("file://C:\\serv\\Apache Tomcat 6.0.20\\bin\\")}; 
     ClassLoader cl_old = Thread.currentThread().getContextClassLoader(); 
     ClassLoader cl_new = new URLClassLoader(urls, cl_old); 
     Class compiledClass = cl_new.loadClass(CLASS_NAME); 
     Method myMethod = compiledClass.getMethod(METHOD_NAME); 
     Object tmp = myMethod.invoke(null); 
    } catch (Exception ex) { 
     Logger.getLogger(ITaskCompile.class.getName()).log(Level.SEVERE, null, ex); 
    } 

我怎樣才能防止無限循環,和邪惡的學生我的應用程序;)

  1. 有沒有辦法在一生中運行該代碼?
  2. 有沒有內存泄漏的風險,我能做些什麼來解決這個問題。
  3. 這是一個很好的解決方案,或者你可以提出更好的建議嗎?

thx。 Tzim

回答

2

我怎樣才能防止無限循環我的應用程序,和邪惡的學生;)

你不能在一個JVM。邪惡的學生特別難以處理,因爲聰明的學生會想出某種方式來顛覆你的控制機制。

1)有沒有辦法在一生中運行該代碼?

不,除非您在單獨的JVM中運行它。

2)有沒有內存泄漏的風險,我能做些什麼來解決這個問題。

是的,沒有什麼可以做的(除了單獨的JVM)。事實上,即使你可以終止留在循環中的學生程序等等,這仍然是一個問題。應用程序可能會導致Java類庫泄漏內存/資源的很多方法...即使在應用程序之後本身已經完成並被GC'ed。

3)這是一個很好的解決方案,或者你可以提出更好的建議嗎?

使用Process和朋友從您的服務器啓動的單獨JVM中運行每個學生應用程序。您將需要編寫主機操作系統特定的東西來設置執行時間限制,並殺死那些死鎖的學生應用程序。另外,你有各種各樣的問題,確保你不會無意中通過觸發太多的JVM來破壞主機性能。

一個更好的答案是爲每個學生提供一臺臺式機或一臺虛擬機,並讓他們做自己的事情。

+0

+1:但有一點需要注意的是,即使在同一個JVM中,您也應該可以使用SecurityManager對學生代碼進行打包。但是您會希望使用以下政策:全部阻止,然後允許選定的操作。 – 2010-01-28 23:19:23

+0

Thx。最糟糕的是我必須用一臺服務器來做到這一點。我會嘗試使用Process。感謝您的回覆。 – tzim 2010-01-28 23:24:25

1

有沒有辦法在一生中運行該代碼?

創建一個監視子進程並終止它的進程,如果它需要很長時間。

有沒有內存泄漏的風險,我該怎麼辦才能解決這個問題。

通過控制分配多少內存(如Sun的JVM的-Xmx參數),您應該能夠在某種程度上這樣做。

這是一個很好的解決方案,或者你可以提出更好的建議嗎?

我不確定解決方案已經提出,但這裏有一個想法。安裝一個SecurityManager,它極大地限制了執行的代碼可以執行的操作,例如訪問文件系統,產生進程等。將它與監視超時的進程結合使用,限制分配的內存,在單獨的用戶帳戶下運行應用程序等。 ,我認爲你可以有一些可行的方法。

您正在尋找的是可能的,但如果僅限於Java,可能並不完全如此。

1

添加到Kaleb的答案中,請確保以嚴格的堆限制運行目標JVM(例如。-Xmx16M)。當然,你會想限制運行的JVM的數量。

1

我目前的解決方案看起來像這樣:

運行代碼:

@RequestMapping("/student/runITask.action") 
public String student_runITask(@ModelAttribute(value = "program") ProgramToCompile program, ModelMap map) { 
    //1. code compile 
    ITaskCompile itcompile = new ITaskCompile(); 
    List<String> errorList = itcompile.compileTask(program.getClassname(), program.getProgram()); 
    Date tmp = new Date(); 
    this.setPathName(program.getClassname() + tmp.hashCode()); 
    //2. if compiled... 
    if (errorList.size() < 1) { 
     try { 
      String[] cmd = {"/bin/sh", "-c", "java -Xmx16M -Xms2M -cp /root/ " + program.getClassname() + "> " + getPathName() + ".data"}; 
      Runtime rt = Runtime.getRuntime(); 
      final Process proc = rt.exec(cmd); 
      Thread.sleep(1000); 
      proc.destroy(); 
      if (proc.exitValue() > 0) { 
       try { 
        killJavaProcesses(); 
        map.addAttribute("comment", "Endless LOOP!"); 
       } catch (Exception ex1) { 
        Logger.getLogger(CompileITaskControler.class.getName()).log(Level.SEVERE, null, ex1); 
       } 
      } else { 
       StringBuffer fileData = new StringBuffer(1000); 
       BufferedReader reader = new BufferedReader(new FileReader("/root/" + getPathName() + ".data")); 
       char[] buf = new char[1024]; 
       int numRead = 0; 
       while ((numRead = reader.read(buf)) != -1) { 
        fileData.append(buf, 0, numRead); 
       } 
       reader.close(); 
       map.addAttribute("comment","Output: <br/><br/><br/><pre>"+fileData.toString()+"</pre>"); 
      } 
     } catch (Exception ex) { 
      try { 
       killJavaProcesses(); 
       map.addAttribute("comment", "Endless LOOP!"); 
      } catch (Exception ex1) { 
       Logger.getLogger(CompileITaskControler.class.getName()).log(Level.SEVERE, null, ex1); 
      } 
     } 
    } else { 
     map.addAttribute("errorList", errorList); 
     map.addAttribute("comment", "PROGRAM NIE ZOSTAŁ URUCHOMIONY"); 

    } //3. return 
    return DISPLAY_COMP_MSG; 


} 

其中killJavaProcesses()看起來像

public void killJavaProcesses() throws IOException, InterruptedException { 
    String[] getProcessList = {"/bin/sh", "-c", "ps | grep java"}; 
    String[] killProcessByIdList = {"/bin/sh", "-c", "kill -9 "}; 
    Runtime rt = Runtime.getRuntime(); 
    Process proc = rt.exec(getProcessList); 
    InputStream inputstream = proc.getInputStream(); 
    InputStreamReader inputstreamreader = new InputStreamReader(inputstream); 
    BufferedReader bufferedreader = new BufferedReader(inputstreamreader); 
    String line2; 
    String kill = new String(); 
    while ((line2 = bufferedreader.readLine()) != null) { 
     kill += line2 + "\n"; 
    } 
    proc.destroy(); 
    String arraykill[] = kill.split("\n"); 
    String element2kill = ""; 
    String[] tmp; 
    if (arraykill.length >= 1) { 
     element2kill = arraykill[arraykill.length - 1].trim().split(" ")[0]; 
    } 
    killProcessByIdList[2] += element2kill; 
    Process proc2 = rt.exec(killProcessByIdList); 
    proc2.waitFor(); 
} 

我不能殺的過程不同。使用proc.destroy()在Ubuntu/Win XP機器上無法正常工作。 現在我將嘗試配置和使用SecurityManager