Compiling and running user code using JavaCompiler and ClassLoader

I am writing a Java learning web application. Using which users can compile their code on my serwer +, run this code. Compiling with JavaCompiler is simple:

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

I manage to load the class with the code:

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

How can I protect my application from an endless cycle and evil students;)

  • Is there a way to run this code over time?
  • Is there a risk of a memory leak, and what can I do to fix it.
  • Is this a good solution, or can you offer something better?

THX Tzim

+6
java classloader java-compiler-api
source share
4 answers

How can I protect my application from an endless cycle and evil students;)

You cannot in one JVM. Angry students are especially difficult because smart ones will come up with some way to undermine your control mechanisms.

1) is there a way to run this code with a lifetime?

Not unless you run it in a separate JVM.

2) is there a risk of a memory leak, and what can I do to fix it.

Yes, there is, and there is nothing you can do about it (except for individual JVMs). Actually, that would be a problem, even if you could kill student programs that get stuck in a loop, etc. There are probably many ways that an application can cause Java class libraries to leak memory / resources ... even after the application itself it was finished and was GC'ed.

3) is a good solution, or can you offer something better?

Run each student application in a separate JVM that you run from your server using Process and friends. You will need to write specific information on the host operating system in order to set a time frame for execution, and kill student applications that are at an impasse. In addition, you have all sorts of problems, making sure that you do not accidentally destroy the performance of the host machine by releasing too many JVMs.

The best answer is to provide each student with a desktop computer or virtual machine and let them do their job.

+2
source share

Is there a way to run this code over time?

Create a process that monitors the child processes and terminates it if it takes too much time.

Is there a risk of a memory leak, and what can I do to fix it.

You should be able to do this to some extent by controlling how much memory is allocated (for example, the -Xmx for the Sun JVM).

Is this a good solution, or can you offer something better?

I'm not sure if a solution has been proposed, but here is the thought. Install SecurityManager , which significantly limits the execution of executable code, for example, access to the file system, appearance processes, etc. Combine this with a process that controls timeouts, limits allocated memory, launches the application under a separate user account, etc., and I think you might have something workable.

What you are looking for is possible, but it may not be quite so if you are limited only to Java.

+1
source share

When adding Caleb to the answer, be sure to run the target JVM with a strict heap limit (e.g. -Xmx16M). And, of course, you will want to limit the number of running JVMs.

+1
source share

My current solution looks like this:

Enter code:

 @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; } 

where killJavaProcesses () looks like this

 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(); } 

I can not kill the process to others. Using proc.destroy () does not work directly on Ubuntu / Win XP machines. Now I will try to configure and use the SecurityManager .

+1
source share

All Articles