All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.stratosphere.client.web.JobSubmissionServlet Maven / Gradle / Ivy

There is a newer version: 0.5.2-hadoop2
Show newest version
/***********************************************************************************************************************
 * Copyright (C) 2010-2013 by the Stratosphere project (http://stratosphere.eu)
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 **********************************************************************************************************************/

package eu.stratosphere.client.web;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import eu.stratosphere.client.program.Client;
import eu.stratosphere.client.program.PackagedProgram;
import eu.stratosphere.client.program.ProgramInvocationException;
import eu.stratosphere.compiler.CompilerException;
import eu.stratosphere.compiler.plan.OptimizedPlan;
import eu.stratosphere.compiler.plandump.PlanJSONDumpGenerator;
import eu.stratosphere.configuration.Configuration;
import eu.stratosphere.nephele.jobgraph.JobGraph;


public class JobSubmissionServlet extends HttpServlet {
	
	/**
	 * Serial UID for serialization interoperability.
	 */
	private static final long serialVersionUID = 8447312301029847397L;

	// ------------------------------------------------------------------------

	public static final String START_PAGE_URL = "launch.html";

	private static final String ACTION_PARAM_NAME = "action";

	private static final String ACTION_SUBMIT_VALUE = "submit";

	private static final String ACTION_RUN_SUBMITTED_VALUE = "runsubmitted";

	private static final String ACTION_BACK_VALUE = "back";

	private static final String JOB_PARAM_NAME = "job";

	private static final String ARGUMENTS_PARAM_NAME = "arguments";

	private static final String SHOW_PLAN_PARAM_NAME = "show_plan";

	private static final String SUSPEND_PARAM_NAME = "suspend";

	private static final Log LOG = LogFactory.getLog(JobSubmissionServlet.class);

	// ------------------------------------------------------------------------

	private final File jobStoreDirectory;				// the directory containing the uploaded jobs

	private final File planDumpDirectory;				// the directory to dump the optimizer plans to

	private final Map submittedJobs;	// map from UIDs to the running jobs

	private final Random rand;							// random number generator for UIDs

	private final Client client;						// the client used to compile and submit jobs


	public JobSubmissionServlet(Configuration nepheleConfig, File jobDir, File planDir) {
		this.client = new Client(nepheleConfig);
		this.jobStoreDirectory = jobDir;
		this.planDumpDirectory = planDir;

		this.submittedJobs = Collections.synchronizedMap(new HashMap());

		this.rand = new Random(System.currentTimeMillis());
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String action = req.getParameter(ACTION_PARAM_NAME);
		if (checkParameterSet(resp, action, "action")) {
			return;
		}

		// decide according to the action
		if (action.equals(ACTION_SUBMIT_VALUE)) {
			// --------------- submit a job -------------------

			// get the parameters
			String jobName = req.getParameter(JOB_PARAM_NAME);
			String args = req.getParameter(ARGUMENTS_PARAM_NAME);
			String showPlan = req.getParameter(SHOW_PLAN_PARAM_NAME);
			String suspendPlan = req.getParameter(SUSPEND_PARAM_NAME);

			// check that all parameters are set
			if (checkParameterSet(resp, jobName, JOB_PARAM_NAME) || checkParameterSet(resp, args, ARGUMENTS_PARAM_NAME)
				|| checkParameterSet(resp, showPlan, SHOW_PLAN_PARAM_NAME)
				|| checkParameterSet(resp, suspendPlan, SUSPEND_PARAM_NAME))
			{
				showErrorPage(resp, "Invalid request, missing parameters.");
				return;
			}

			boolean show = Boolean.parseBoolean(showPlan);
			boolean suspend = Boolean.parseBoolean(suspendPlan);

			// check, if the jar exists
			File jarFile = new File(jobStoreDirectory, jobName);
			if (!jarFile.exists()) {
				showErrorPage(resp, "The jar file + '" + jarFile.getPath() + "' does not exist.");
				return;
			}

			// parse the arguments
			List params = null;
			try {
				params = tokenizeArguments(args);
			} catch (IllegalArgumentException iaex) {
				showErrorPage(resp, "The arguments contain an unterminated quoted string.");
				return;
			}

			String assemblerClass = null;
			if (params.size() >= 2 && params.get(0).equals("assembler")) {
				assemblerClass = params.get(1);
				params.remove(0);
				params.remove(0);
			}

			// create the plan
			String[] options = params.isEmpty() ? new String[0] : (String[]) params.toArray(new String[params.size()]);
			PackagedProgram program;
			OptimizedPlan optPlan;
			
			try {
				if (assemblerClass == null) {
					program = new PackagedProgram(jarFile, options);
				} else {
					program = new PackagedProgram(jarFile, assemblerClass, options);
				}
				
				optPlan = client.getOptimizedPlan(program, -1);
				
				if (optPlan == null) {
					throw new Exception("The optimized plan could not be produced.");
				}
			}
			catch (ProgramInvocationException e) {
				// collect the stack trace
				StringWriter sw = new StringWriter();
				PrintWriter w = new PrintWriter(sw);
				
				if (e.getCause() == null) {
					e.printStackTrace(w);
				} else {
					e.getCause().printStackTrace(w);
				}

				showErrorPage(resp, "An error occurred while invoking the program:

" + e.getMessage() + "
" + "

" + sw.toString() + "
"); return; } catch (CompilerException cex) { // collect the stack trace StringWriter sw = new StringWriter(); PrintWriter w = new PrintWriter(sw); cex.printStackTrace(w); showErrorPage(resp, "An error occurred in the compiler:

" + cex.getMessage() + "
" + (cex.getCause()!= null?"Caused by: " + cex.getCause().getMessage():"") + "

" + sw.toString() + "
"); return; } catch (Throwable t) { // collect the stack trace StringWriter sw = new StringWriter(); PrintWriter w = new PrintWriter(sw); t.printStackTrace(w); showErrorPage(resp, "An unexpected error occurred:

" + t.getMessage() + "

"
					+ sw.toString() + "
"); return; } // redirect according to our options if (show) { // we have a request to show the plan // create a UID for the job Long uid = null; do { uid = Math.abs(this.rand.nextLong()); } while (this.submittedJobs.containsKey(uid)); // dump the job to a JSON file String planName = uid + ".json"; File jsonFile = new File(this.planDumpDirectory, planName); new PlanJSONDumpGenerator().dumpOptimizerPlanAsJSON(optPlan, jsonFile); // submit the job only, if it should not be suspended if (!suspend) { try { this.client.run(program, optPlan, false); } catch (Throwable t) { LOG.error("Error submitting job to the job-manager.", t); showErrorPage(resp, t.getMessage()); return; } finally { program.deleteExtractedLibraries(); } } else { try { this.submittedJobs.put(uid, this.client.getJobGraph(program, optPlan)); } catch (ProgramInvocationException piex) { LOG.error("Error creating JobGraph from optimized plan.", piex); showErrorPage(resp, piex.getMessage()); return; } catch (Throwable t) { LOG.error("Error creating JobGraph from optimized plan.", t); showErrorPage(resp, t.getMessage()); return; } } // redirect to the plan display page resp.sendRedirect("showPlan?id=" + uid + "&suspended=" + (suspend ? "true" : "false")); } else { // don't show any plan. directly submit the job and redirect to the // nephele runtime monitor try { client.run(program, -1, false); } catch (Exception ex) { LOG.error("Error submitting job to the job-manager.", ex); // HACK: Is necessary because Message contains whole stack trace String errorMessage = ex.getMessage().split("\n")[0]; showErrorPage(resp, errorMessage); return; } finally { program.deleteExtractedLibraries(); } resp.sendRedirect(START_PAGE_URL); } } else if (action.equals(ACTION_RUN_SUBMITTED_VALUE)) { // --------------- run a job that has been submitted earlier, but was ------------------- // --------------- not executed because of a plan display ------------------- String id = req.getParameter("id"); if (checkParameterSet(resp, id, "id")) { return; } Long uid = null; try { uid = Long.parseLong(id); } catch (NumberFormatException nfex) { showErrorPage(resp, "An invalid id for the job was provided."); return; } // get the retained job JobGraph job = submittedJobs.remove(uid); if (job == null) { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "No job with the given uid was retained for later submission."); return; } // submit the job try { client.run(job, false); } catch (Exception ex) { LOG.error("Error submitting job to the job-manager.", ex); resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); // HACK: Is necessary because Message contains whole stack trace String errorMessage = ex.getMessage().split("\n")[0]; resp.getWriter().print(errorMessage); // resp.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage()); return; } // redirect to the start page resp.sendRedirect(START_PAGE_URL); } else if (action.equals(ACTION_BACK_VALUE)) { // remove the job from the map String id = req.getParameter("id"); if (checkParameterSet(resp, id, "id")) { return; } Long uid = null; try { uid = Long.parseLong(id); } catch (NumberFormatException nfex) { showErrorPage(resp, "An invalid id for the job was provided."); return; } // remove the retained job submittedJobs.remove(uid); // redirect to the start page resp.sendRedirect(START_PAGE_URL); } else { showErrorPage(resp, "Invalid action specified."); return; } } /** * Prints the error page, containing the given message. * * @param resp * The response handler. * @param message * The message to display. * @throws IOException * Thrown, if the error page could not be printed due to an I/O problem. */ private void showErrorPage(HttpServletResponse resp, String message) throws IOException { resp.setStatus(HttpServletResponse.SC_OK); resp.setContentType(GUIServletStub.CONTENT_TYPE_HTML); PrintWriter writer = resp.getWriter(); writer .println(""); writer.println(""); writer.println(""); writer.println(" Launch Job - Error"); writer.println(" "); writer.println(" "); writer.println(""); writer.println(""); writer.println("
"); writer .println("

\"StratosphereNephele and PACTs Query Interface

"); writer.println("
"); writer.println("
"); writer.println("

"); writer.println(message); writer.println("



"); writer.println("
"); writer.println(" "); writer.println("
"); writer.println("
"); writer.println(""); writer.println(""); } /** * Checks the given parameter. If it is null, it prints the error page. * * @param resp * The response handler. * @param parameter * The parameter to check. * @param parameterName * The name of the parameter, to describe it in the error message. * @return True, if the parameter is null, false otherwise. * @throws IOException * Thrown, if the error page could not be printed. */ private boolean checkParameterSet(HttpServletResponse resp, String parameter, String parameterName) throws IOException { if (parameter == null) { showErrorPage(resp, "The parameter '" + parameterName + "' is not set."); return true; } else { return false; } } /** * Utility method that takes the given arguments, splits them at the whitespaces (space and tab) and * turns them into an array of Strings. Other than the StringTokenizer, this method * takes care of quotes, such that quoted passages end up being one string. * * @param args * The string to be split. * @return The array of split strings. */ private static final List tokenizeArguments(String args) { List list = new ArrayList(); StringBuilder curr = new StringBuilder(); int pos = 0; boolean quoted = false; while (pos < args.length()) { char c = args.charAt(pos); if ((c == ' ' || c == '\t') && !quoted) { if (curr.length() > 0) { list.add(curr.toString()); curr.setLength(0); } } else if (c == '"') { quoted = !quoted; } else { curr.append(c); } pos++; } if (quoted) { throw new IllegalArgumentException("Unterminated quoted string."); } if (curr.length() > 0) { list.add(curr.toString()); } return list; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy