使用JavaCompiler和ClassLoader编译和运行用户代码

我正在为java学习编写web应用程序。 使用哪些用户可以在我的服务器上编译他们的代码+运行该代码。 使用JavaCompiler进行编译很容易:

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); DiagnosticCollector diagnostics = new DiagnosticCollector(); CompilationTask task = compiler.getTask(null, null, diagnostics, null, null, prepareFile(nazwa, content)); task.call(); List returnErrors = new ArrayList(); 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. 这是一个很好的解决方案,还是你能提出更好的建议?

谢谢。 Tzim

我如何保护我的应用程序免受无限循环和邪恶的学生;)

你不能在一个JVM中。 邪恶的学生特别难以应对,因为聪明的学生会想出一些方法来颠覆你的控制机制。

1)有没有办法运行该代码一生?

不,除非您在单独的JVM中运行它。

2)是否存在内存泄漏的风险,我该怎么做才能解决这个问题。

是的,没有什么可以做的(除了单独的JVM)。 实际上,即使您可以杀死陷入循环等的学生程序,这也是一个问题。应用程序可能有很多方法导致Java类库泄漏内存/资源……即使在应用程序之后本身已经完成并且已经过GC。

3)这是一个很好的解决方案,还是你可以提出更好的建议?

在使用Process和friends从服务器启动的单独JVM中运行每个学生应用程序。 您将需要编写主机操作系统特定的东西来设置执行时间限制,并杀死死锁的学生应用程序。 此外,您还有各种各样的问题,确保您不会通过触发太多JVM而意外地破坏主机性能。

更好的答案是为每个学生提供台式计算机或虚拟机,让他们自己做。

有没有办法运行该代码一生?

创建一个监视子进程的进程,如果花费的时间太长则终止它。

是否存在内存泄漏的风险,我该怎么做才能解决这个问题。

在某种程度上,你应该可以通过控制分配多少内存来实现这一点(比如Sun的JVM的-Xmx参数)。

这是一个很好的解决方案,还是你能提出更好的建议?

我不确定是否已提出解决方案,但这是一个想法。 安装一个极大限制执行代码可以执行的操作的SecurityManager ,例如访问文件系统,生成进程等。将其与监视超时,限制分配的内存,在单独的用户帐户下运行应用程序等的进程相结合。 ,我认为你可以有一些可行的东西。

您正在寻找的是可能的,但如果仅限于Java,则可能不完全如此。

添加到Kaleb的答案,请确保以严格的堆限制运行目标JVM(例如-Xmx16M)。 当然,您需要限制运行的JVM数量。

我目前的解决方案如下:

运行代码:

 @RequestMapping("/student/runITask.action") public String student_runITask(@ModelAttribute(value = "program") ProgramToCompile program, ModelMap map) { //1. code compile ITaskCompile itcompile = new ITaskCompile(); List 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: 


"+fileData.toString()+"

"); } } 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 。