org.pentaho.di.www.GetJobStatusServlet Maven / Gradle / Ivy
Show all versions of kettle-engine Show documentation
/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2019 by Hitachi Vantara : 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 java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.common.annotations.VisibleForTesting;
import org.owasp.encoder.Encode;
import org.pentaho.di.cluster.HttpUtil;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.util.EnvUtil;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.gui.Point;
import org.pentaho.di.core.logging.KettleLogStore;
import org.pentaho.di.core.xml.XMLHandler;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.job.Job;
import org.pentaho.di.www.cache.CarteStatusCache;
public class GetJobStatusServlet extends BaseHttpServlet implements CartePluginInterface {
private static Class> PKG = GetJobStatusServlet.class; // for i18n purposes, needed by Translator2!!
private static final long serialVersionUID = 3634806745372015720L;
public static final String CONTEXT_PATH = "/kettle/jobStatus";
private static final byte[] XML_HEADER =
XMLHandler.getXMLHeader( Const.XML_ENCODING ).getBytes( Charset.forName( Const.XML_ENCODING ) );
@VisibleForTesting
CarteStatusCache cache = CarteStatusCache.getInstance();
public GetJobStatusServlet() {
}
public GetJobStatusServlet( JobMap jobMap ) {
super( jobMap );
}
/**
/kettle/jobStatus
GET
Retrieves status of the specified job.
Status is returned as HTML or XML output depending on the input parameters.
Status contains information about last execution of the job.
Example Request:
GET /kettle/jobStatus/?name=dummy_job&xml=Y
Parameters
name
description
type
name
Name of the job to be used for status generation.
query
xml
Boolean flag which defines output format Y
forces XML output to be generated.
HTML is returned otherwise.
boolean, optional
id
Carte id of the job to be used for status generation.
query, optional
from
Start line number of the execution log to be included into response.
integer, optional
Response Body
element:
(custom)
media types:
text/xml, text/html
Response XML or HTML response containing details about the job specified.
If an error occurs during method invocation result
field of the response
will contain ERROR
status.
Example Response:
dummy_job
a4d54106-25db-41c5-b9f8-73afd42766a6
Finished
<![CDATA[H4sIAAAAAAAAADMyMDTRNzTUNzRXMDC3MjS2MjJQ0FVIKc3NrYzPyk8CsoNLEotKFPLTFEDc1IrU5NKSzPw8Xi4j4nRm5qUrpOaVFFUqRLuE+vpGxhKj0y0zL7M4IzUFYieybgWNotTi0pwS2+iSotLUWE1iTPNCdrhCGtRsXi4AOMIbLPwAAAA=]]>
0
20
0
0
0
0
0
0
0
0
0
0
Y
0
N
null
Status Codes
code
description
200
Request was processed.
500
Internal 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, "GetJobStatusServlet.Log.JobStatusRequested" ) );
}
String jobName = request.getParameter( "name" );
String id = request.getParameter( "id" );
String root = request.getRequestURI() == null ? StatusServletUtils.PENTAHO_ROOT
: request.getRequestURI().substring( 0, request.getRequestURI().indexOf( CONTEXT_PATH ) );
String prefix = isJettyMode() ? StatusServletUtils.STATIC_PATH : root + StatusServletUtils.RESOURCES_PATH;
boolean useXML = "Y".equalsIgnoreCase( request.getParameter( "xml" ) );
int startLineNr = Const.toInt( request.getParameter( "from" ), 0 );
response.setStatus( HttpServletResponse.SC_OK );
if ( useXML ) {
response.setContentType( "text/xml" );
response.setCharacterEncoding( Const.XML_ENCODING );
} else {
response.setContentType( "text/html;charset=UTF-8" );
}
// ID is optional...
//
Job job;
CarteObjectEntry entry;
if ( Utils.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 {
// Actually, just providing the ID should be enough to identify the job
//
if ( Utils.isEmpty( jobName ) ) {
// Take the ID into account!
//
job = getJobMap().findJob( id );
} else {
entry = new CarteObjectEntry( jobName, id );
job = getJobMap().getJob( entry );
if ( job != null ) {
jobName = job.getJobname();
}
}
}
if ( job != null ) {
if ( useXML ) {
try {
OutputStream out = null;
byte[] data = null;
String logId = job.getLogChannelId();
boolean finishedOrStopped = job.isFinished() || job.isStopped();
if ( finishedOrStopped && ( data = cache.get( logId, startLineNr ) ) != null ) {
response.setContentLength( XML_HEADER.length + data.length );
out = response.getOutputStream();
out.write( XML_HEADER );
out.write( data );
out.flush();
} else {
int lastLineNr = KettleLogStore.getLastBufferLineNr();
String logText = getLogText( job, startLineNr, lastLineNr );
response.setContentType( "text/xml" );
response.setCharacterEncoding( Const.XML_ENCODING );
SlaveServerJobStatus jobStatus = new SlaveServerJobStatus( jobName, id, job.getStatus() );
jobStatus.setFirstLoggingLineNr( startLineNr );
jobStatus.setLastLoggingLineNr( lastLineNr );
jobStatus.setLogDate( job.getLogDate() );
// The log can be quite large at times, we are going to putIfAbsent a base64 encoding around a compressed
// stream
// of bytes to handle this one.
String loggingString = HttpUtil.encodeBase64ZippedString( logText );
jobStatus.setLoggingString( loggingString );
// Also set the result object...
//
jobStatus.setResult( job.getResult() ); // might be null
String xml = jobStatus.getXML();
data = xml.getBytes( Charset.forName( Const.XML_ENCODING ) );
out = response.getOutputStream();
response.setContentLength( XML_HEADER.length + data.length );
out.write( XML_HEADER );
out.write( data );
out.flush();
if ( finishedOrStopped && ( jobStatus.isFinished() || jobStatus.isStopped() ) && logId != null ) {
cache.put( logId, xml, startLineNr );
}
}
response.flushBuffer();
} catch ( KettleException e ) {
throw new ServletException( "Unable to get the job status in XML format", e );
}
} else {
PrintWriter out = response.getWriter();
int lastLineNr = KettleLogStore.getLastBufferLineNr();
int tableBorder = 0;
response.setContentType( "text/html" );
out.println( "" );
out.println( "" );
out
.println( ""
+ BaseMessages.getString( PKG, "GetJobStatusServlet.KettleJobStatus" ) + " " );
if ( EnvUtil.getSystemProperty( Const.KETTLE_CARTE_REFRESH_STATUS, "N" ).equalsIgnoreCase( "Y" ) ) {
out.println( "" );
}
out.println( "" );
if ( isJettyMode() ) {
out.println( "" );
} else {
out.print( StatusServletUtils.getPentahoStyles( root ) );
}
out.println( "" );
out.println( "" );
out.println( "" );
out.println( "" + Encode.forHtml( BaseMessages.getString( PKG, "GetJobStatusServlet.JobStatus", jobName ) ) + "" );
out.println( "" );
try {
out.println( "" );
out.println( "" );
out.print( "" );
out.print( "" );
out.print( BaseMessages.getString( PKG, "CarteStatusServlet.BackToCarteStatus" ) + "" );
out.println( "" );
out.println( "" );
out.println( "" );
out.println( "" );
out.println( "" );
out.println( "" );
out.println( "" );
out.println( "" );
out.print( " "
+ BaseMessages.getString( PKG, "TransStatusServlet.CarteObjectId" ) + " "
+ BaseMessages.getString( PKG, "TransStatusServlet.TransStatus" ) + " "
+ BaseMessages.getString( PKG, "TransStatusServlet.LastLogDate" ) + " " );
out.print( "" );
out.print( "" + Const.NVL( Encode.forHtml( id ), "" ) + " " );
out.print( "" + job.getStatus() + " " );
String dateStr = XMLHandler.date2string( job.getLogDate() );
out.print( "" + dateStr.substring( 0, dateStr.indexOf( ' ' ) ) + " " );
out.print( " " );
out.print( "
" );
out.print( "" );
out.println( "" );
out.print( "" );
out.print( ""
+ "" );
out.print( "" );
out.println( "" );
out.print( ""
+ BaseMessages.getString( PKG, "TransStatusServlet.ShowAsXml" ) + "" );
out.print( "" );
out.print( "" );
out.print( "" );
out.print( "" );
out.print( "" );
out.print( "Canvas preview" );
// Show job image?
//
Point max = job.getJobMeta().getMaximum();
//max.x += 20;
max.y += 20;
out
.print( "" );
out.print( "" );
// out.print(""
// + BaseMessages.getString(PKG, "GetJobImageServlet.GetJobImage") + "");
// out.print("");
// Put the logging below that.
out.print( "
" );
out.print( "Job log" );
out.println( "" );
out.print( "" );
out.println( "" );
out.println( " " );
} catch ( Exception ex ) {
out.println( "" );
out.println( Encode.forHtml( Const.getStackTracker( ex ) ) );
out.println( "
" );
}
out.println( "" );
out.println( "" );
out.println( "" );
}
} else {
PrintWriter out = response.getWriter();
if ( useXML ) {
out.println( new WebResult( WebResult.STRING_ERROR, BaseMessages.getString(
PKG, "StartJobServlet.Log.SpecifiedJobNotFound", jobName, id ) ) );
} else {
out.println( "Job " + Encode.forHtml( "\'" + jobName + "\'" ) + " could not be found.
" );
out.println( ""
+ BaseMessages.getString( PKG, "JobStatusServlet.BackToStatusPage" ) + "" );
}
}
}
public String toString() {
return "Job Status Handler";
}
public String getService() {
return CONTEXT_PATH + " (" + toString() + ")";
}
public String getContextPath() {
return CONTEXT_PATH;
}
private String getLogText( Job job, int startLineNr, int lastLineNr ) throws KettleException {
try {
return KettleLogStore.getAppender().getBuffer(
job.getLogChannel().getLogChannelId(), false, startLineNr, lastLineNr ).toString();
} catch ( OutOfMemoryError error ) {
throw new KettleException( "Log string is too long" );
}
}
}