【问题标题】:compiling and running user code with JavaCompiler and ClassLoader使用 JavaCompiler 和 ClassLoader 编译和运行用户代码
【发布时间】:2010-02-01 02:32:17
【问题描述】:

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

    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. 这是一个好的解决方案,还是您能提出更好的建议?

谢谢。 齐姆

【问题讨论】:

    标签: java classloader java-compiler-api


    【解决方案1】:

    我怎样才能保护我的应用免受死循环和邪恶学生的侵害;)

    你不能在一个 JVM 中。坏学生特别难对付,因为聪明的学生会想办法颠覆你的控制机制。

    1) 有什么方法可以终身运行该代码?

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

    2) 内存泄漏是否存在风险,我能做些什么来解决这个问题。

    是的,但您对此无能为力(除了单独的 JVM)。事实上,即使你可以杀死陷入循环等的学生程序,这也会是一个问题。应用程序可能有很多方法可以导致 Java 类库泄漏内存/资源......即使在应用程序之后本身已经完成并被 GC'ed。

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

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

    更好的答案是为每个学生提供一台台式电脑或虚拟机,让他们做自己的事情。

    【讨论】:

    • +1:但需要注意的是,您应该能够使用 SecurityManager 对学生代码进行沙箱处理,即使在同一个 JVM 中也是如此。但您会希望使用以下策略:全部阻止,然后允许选定的操作。
    • 谢谢。呵呵,最糟糕的是我必须用一台服务器来做这件事。我会尝试使用 Process。谢谢你的回复。
    【解决方案2】:

    有什么方法可以终身运行该代码?

    创建一个监控子进程的进程,如果耗时过长则终止它。

    内存泄漏是否有任何风险,我可以做些什么来解决这个问题。

    在某种程度上,您应该能够通过控制分配多少内存来做到这一点(例如 Sun JVM 的 -Xmx 参数)。

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

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

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

    【讨论】:

      【解决方案3】:

      除了 Kaleb 的回答之外,请务必使用严格的堆限制(例如 -Xmx16M)运行目标 JVM。当然,您还需要限制运行的 JVM 数量。

      【讨论】:

        【解决方案4】:

        我目前的解决方案是这样的:

        运行代码:

        @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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-01-29
          • 1970-01-01
          • 2013-10-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多