diff --git a/pentaho-kettle/src/main/java/org/pentaho/di/www/StartExecutionTransServlet.java b/pentaho-kettle/src/main/java/org/pentaho/di/www/StartExecutionTransServlet.java new file mode 100644 index 0000000..030185e --- /dev/null +++ b/pentaho-kettle/src/main/java/org/pentaho/di/www/StartExecutionTransServlet.java @@ -0,0 +1,272 @@ +/*! ****************************************************************************** + * + * Pentaho Data Integration + * + * Copyright (C) 2002-2013 by Pentaho : http://www.pentaho.com + * + ******************************************************************************* + * + * 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 org.pentaho.di.www; + +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.Encoder; +import org.pentaho.di.core.Const; +import org.pentaho.di.core.exception.KettleException; +import org.pentaho.di.core.xml.XMLHandler; +import org.pentaho.di.i18n.BaseMessages; +import org.pentaho.di.trans.Trans; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URLEncoder; + +public class StartExecutionTransServlet extends BaseHttpServlet implements CartePluginInterface { + private static Class PKG = StartExecutionTransServlet.class; // for i18n purposes, needed by Translator2!! + + private static final long serialVersionUID = 3634806745372015720L; + public static final String CONTEXT_PATH = "/kettle/startExec"; + + public StartExecutionTransServlet() { + } + + public StartExecutionTransServlet(TransformationMap transformationMap) { + super(transformationMap); + } + + /** + *
+ *

/kettle/startExec

+ * + *

GET

+ *

Starts transformation. If the transformation is not ready, an error is returned.

+ *

+ *

Example Request:
+ *

+     * GET /kettle/startExec/?name=dummy-trans&xml=Y
+     * 
+ *

+ *

+ *

Parameters

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
namedescriptiontype
nameName of the transformation to be executed.query
xmlBoolean flag which sets the output format required. Use Y to receive XML response.boolean, optional
idCarte transformation ID of the transformation to be executed. This parameter is optional when xml=Y is used.query, optional
+ *

+ *

Response Body

+ *

+ * + * + * + * + * + * + * + * + * + * + * + *
text:HTML
media types:text/xml, text/html
+ *

Response XML or HTML containing operation result. When using xml=Y result field indicates whether + * operation was successful (OK) or not (ERROR).

+ *

+ *

Example Response:

+ *
+     * 
+     * 
+     * OK
+     * 
+     * 
+     * 
+     * 
+ *

+ *

Status Codes

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
codedescription
200Request was processed.
500Internal server error occurs during request processing.
+ *
+ */ + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, + IOException { + if (isJettyMode() && !request.getContextPath().startsWith(CONTEXT_PATH)) { + return; + } + + if (log.isDebug()) { + logDebug("Start execution of transformation requested"); + } + response.setStatus(HttpServletResponse.SC_OK); + + String transName = request.getParameter("name"); + String id = request.getParameter("id"); + boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml")); + + Encoder encoder = ESAPI.encoder(); + + PrintWriter out = response.getWriter(); + if (useXML) { + response.setContentType("text/xml"); + out.print(XMLHandler.getXMLHeader(Const.XML_ENCODING)); + } else { + response.setContentType("text/html;charset=UTF-8"); + out.println(""); + out.println(""); + out.println("" + + BaseMessages.getString(PKG, "PrepareExecutionTransServlet.TransPrepareExecution") + ""); + out.println(""); + out.println(""); + out.println(""); + out.println(""); + } + + try { + // ID is optional... + // + Trans trans; + CarteObjectEntry entry; + if (Const.isEmpty(id)) { + // get the first transformation that matches... + // + entry = getTransformationMap().getFirstCarteObjectEntry(transName); + if (entry == null) { + trans = null; + } else { + id = entry.getId(); + trans = getTransformationMap().getTransformation(entry); + } + } else { + // Take the ID into account! + // + entry = new CarteObjectEntry(transName, id); + trans = getTransformationMap().getTransformation(entry); + } + + if (trans != null) { + if (trans.isReadyToStart()) { + startThreads(trans); + + if (useXML) { + out.println(WebResult.OK.getXML()); + } else { + out + .println("

Transformation '" + + encoder.encodeForHTML(transName) + "' has been executed.

"); + out.println("Back to the transformation status page

"); + } + } else { + String message = + "The specified transformation [" + + transName + "] is not ready to be started. (Was not prepared for execution)"; + if (useXML) { + out.println(new WebResult(WebResult.STRING_ERROR, message)); + } else { + out.println("

" + encoder.encodeForHTML(message) + "

"); + out.println("" + + BaseMessages.getString(PKG, "TransStatusServlet.BackToStatusPage") + "

"); + } + } + } else { + if (useXML) { + out.println(new WebResult(WebResult.STRING_ERROR, BaseMessages.getString( + PKG, "TransStatusServlet.Log.CoundNotFindSpecTrans", transName))); + } else { + out.println("

" + + encoder.encodeForHTML(BaseMessages.getString( + PKG, "TransStatusServlet.Log.CoundNotFindTrans", transName)) + "

"); + out.println("" + + BaseMessages.getString(PKG, "TransStatusServlet.BackToStatusPage") + "

"); + } + } + } catch (Exception ex) { + if (useXML) { + out.println(new WebResult( + WebResult.STRING_ERROR, "Unexpected error during transformation execution preparation:" + + Const.CR + Const.getStackTracker(ex))); + } else { + out.println("

"); + out.println("

");
+                out.println(encoder.encodeForHTML(Const.getStackTracker(ex)));
+                out.println("
"); + } + } + + if (!useXML) { + out.println("

"); + out.println(""); + out.println(""); + } + } + + public String toString() { + return "Start transformation"; + } + + public String getService() { + return CONTEXT_PATH + " (" + toString() + ")"; + } + + protected void startThreads(Trans trans) throws KettleException { + trans.startThreads(); + } + + public String getContextPath() { + return CONTEXT_PATH; + } + +} diff --git a/pentaho-kettle/src/main/java/org/pentaho/di/www/StartJobServlet.java b/pentaho-kettle/src/main/java/org/pentaho/di/www/StartJobServlet.java new file mode 100644 index 0000000..45c8a53 --- /dev/null +++ b/pentaho-kettle/src/main/java/org/pentaho/di/www/StartJobServlet.java @@ -0,0 +1,304 @@ +/*! ****************************************************************************** + * + * Pentaho Data Integration + * + * Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com + * + ******************************************************************************* + * + * 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 org.pentaho.di.www; + +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.Encoder; +import org.pentaho.di.core.Const; +import org.pentaho.di.core.exception.KettleException; +import org.pentaho.di.core.logging.KettleLogStore; +import org.pentaho.di.core.logging.LoggingObjectType; +import org.pentaho.di.core.logging.SimpleLoggingObject; +import org.pentaho.di.core.xml.XMLHandler; +import org.pentaho.di.i18n.BaseMessages; +import org.pentaho.di.job.Job; +import org.pentaho.di.job.JobConfiguration; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URLEncoder; +import java.util.UUID; + +public class StartJobServlet extends BaseHttpServlet implements CartePluginInterface { + private static Class PKG = StartJobServlet.class; // for i18n purposes, + // needed by + // Translator2!! + + private static final long serialVersionUID = -8487225953910464032L; + + public static final String CONTEXT_PATH = "/kettle/startJob"; + + public StartJobServlet() { + } + + public StartJobServlet(JobMap jobMap) { + super(jobMap); + } + + /** + *

+ *

/kettle/startJob

+ * + *

GET

+ *

Starts the job. If the job cannot be started, an error is returned.

+ *

+ *

Example Request:
+ *

+     * GET /kettle/startJob/?name=dummy_job&xml=Y
+     * 
+ *

+ *

+ *

Parameters

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
namedescriptiontype
nameName of the job to be executed.query
xmlBoolean flag which sets the output format required. Use Y to receive XML response.boolean, optional
idCarte job ID of the job to be executed. This parameter is optional when xml=Y is used.query, optional
+ *

+ *

Response Body

+ *

+ * + * + * + * + * + * + * + * + * + * + * + *
text:HTML
media types:text/xml, text/html
+ *

Response XML or HTML containing operation result. When using xml=Y result field indicates whether + * operation was successful (OK) or not (ERROR).

+ *

+ *

Example Response:

+ *
+     * 
+     * 
+     * OK
+     * Job [dummy_job] was started.
+     * abd61143-8174-4f27-9037-6b22fbd3e229
+     * 
+     * 
+ *

+ *

Status Codes

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
codedescription
200Request was processed.
500Internal server error occurs during request processing.
+ *
+ */ + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, + IOException { + if (isJettyMode() && !request.getContextPath().startsWith(CONTEXT_PATH)) { + return; + } + + if (log.isDebug()) { + logDebug(BaseMessages.getString(PKG, "StartJobServlet.Log.StartJobRequested")); + } + + String jobName = request.getParameter("name"); + String id = request.getParameter("id"); + boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml")); + + response.setStatus(HttpServletResponse.SC_OK); + + Encoder encoder = ESAPI.encoder(); + + PrintWriter out = response.getWriter(); + if (useXML) { + response.setContentType("text/xml"); + response.setCharacterEncoding(Const.XML_ENCODING); + out.print(XMLHandler.getXMLHeader(Const.XML_ENCODING)); + } else { + response.setContentType("text/html;charset=UTF-8"); + out.println(""); + out.println(""); + out.println("Start job"); + out.println(""); + out.println(""); + out.println(""); + out.println(""); + } + + try { + // ID is optional... + // + Job job; + CarteObjectEntry entry; + if (Const.isEmpty(id)) { + // get the first job that matches... + // + entry = getJobMap().getFirstCarteObjectEntry(jobName); + if (entry == null) { + job = null; + } else { + id = entry.getId(); + job = getJobMap().getJob(entry); + } + } else { + // Take the ID into account! + // + entry = new CarteObjectEntry(jobName, id); + job = getJobMap().getJob(entry); + } + + if (job != null) { + // First see if this job already ran to completion. + // If so, we get an exception is we try to start() the job thread + // + if (job.isInitialized() && !job.isActive()) { + // Re-create the job from the jobMeta + // + // We might need to re-connect to the repository + // + if (job.getRep() != null && !job.getRep().isConnected()) { + if (job.getRep().getUserInfo() != null) { + job.getRep().connect( + job.getRep().getUserInfo().getLogin(), job.getRep().getUserInfo().getPassword()); + } else { + job.getRep().connect(null, null); + } + } + + // Create a new job object to start from a sane state. Then replace + // the new job in the job map + // + synchronized (this) { + JobConfiguration jobConfiguration = getJobMap().getConfiguration(jobName); + + String carteObjectId = UUID.randomUUID().toString(); + SimpleLoggingObject servletLoggingObject = + new SimpleLoggingObject(CONTEXT_PATH, LoggingObjectType.CARTE, null); + servletLoggingObject.setContainerObjectId(carteObjectId); + + Job newJob = new Job(job.getRep(), job.getJobMeta(), servletLoggingObject); + newJob.setLogLevel(job.getLogLevel()); + + // Discard old log lines from the old job + // + KettleLogStore.discardLines(job.getLogChannelId(), true); + + getJobMap().replaceJob(entry, newJob, jobConfiguration); + job = newJob; + } + } + + runJob(job); + + String message = BaseMessages.getString(PKG, "StartJobServlet.Log.JobStarted", jobName); + if (useXML) { + out.println(new WebResult(WebResult.STRING_OK, message, id).getXML()); + } else { + + out.println("

" + encoder.encodeForHTML(message) + "

"); + out.println("" + + BaseMessages.getString(PKG, "JobStatusServlet.BackToJobStatusPage") + "

"); + } + } else { + String message = BaseMessages.getString(PKG, "StartJobServlet.Log.SpecifiedJobNotFound", jobName); + if (useXML) { + out.println(new WebResult(WebResult.STRING_ERROR, message)); + } else { + out.println("

" + encoder.encodeForHTML(message) + "

"); + out.println("" + + BaseMessages.getString(PKG, "TransStatusServlet.BackToStatusPage") + "

"); + } + } + } catch (Exception ex) { + if (useXML) { + out.println(new WebResult(WebResult.STRING_ERROR, BaseMessages.getString( + PKG, "StartJobServlet.Error.UnexpectedError", Const.CR + Const.getStackTracker(ex)))); + } else { + out.println("

"); + out.println("

");
+                out.println(encoder.encodeForHTML(Const.getStackTracker(ex)));
+                out.println("
"); + } + } + + if (!useXML) { + out.println("

"); + out.println(""); + out.println(""); + } + } + + public String toString() { + return "Start job"; + } + + public String getService() { + return CONTEXT_PATH + " (" + toString() + ")"; + } + + protected void runJob(Job job) throws KettleException { + job.start(); // runs the thread in the background... + } + + public String getContextPath() { + return CONTEXT_PATH; + } + +}