org.pentaho.di.www.GetTransStatusServlet 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.trans.Trans;
import org.pentaho.di.trans.step.BaseStepData.StepExecutionStatus;
import org.pentaho.di.trans.step.StepInterface;
import org.pentaho.di.trans.step.StepStatus;
import org.pentaho.di.www.cache.CarteStatusCache;
public class GetTransStatusServlet extends BaseHttpServlet implements CartePluginInterface {
private static Class> PKG = GetTransStatusServlet.class; // for i18n purposes, needed by Translator2!!
private static final long serialVersionUID = 3634806745372015720L;
public static final String CONTEXT_PATH = "/kettle/transStatus";
public static final String SEND_RESULT = "sendResult";
private static final byte[] XML_HEADER =
XMLHandler.getXMLHeader( Const.XML_ENCODING ).getBytes( Charset.forName( Const.XML_ENCODING ) );
@VisibleForTesting
CarteStatusCache cache = CarteStatusCache.getInstance();
public GetTransStatusServlet() {
}
public GetTransStatusServlet( TransformationMap transformationMap ) {
super( transformationMap );
}
/**
/kettle/transStatus
GET
Retrieves status of the specified transformation. Status is returned as HTML or XML output
depending on the input parameters. Status contains information about last execution of the transformation.
Example Request:
GET /kettle/transStatus/?name=dummy-trans&xml=Y
Parameters
name
description
type
name
Name of the transformation 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 transformation 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 transformation specified.
If an error occurs during method invocation result
field of the response
will contain ERROR
status.
Example Response:
dummy-trans
c56961b2-c848-49b8-abde-76c8015e29b0
Stopped
N
Dummy (do nothing)
0 0
0 0
0 0
0 0
Stopped 0.0
- - Y
N
0
37
0
0
0
0
0
0
0
0
0
0
Y
0
Y
10e2c832-07da-409a-a5ba-4b90a234e957
<![CDATA[H4sIAAAAAAAAADMyMDTRNzTUNzJRMDSyMrC0MjFV0FVIKc3NrdQtKUrMKwbyXDKLCxJLkjMy89IViksSi0pSUxTS8osUwPJARm5iSWZ+nkI0kq5YXi4AQVH5bFoAAAA=]]>
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, "TransStatusServlet.Log.TransStatusRequested" ) );
}
String transName = 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.setCharacterEncoding( "UTF-8" );
response.setContentType( "text/html;charset=UTF-8" );
}
// ID is optional...
//
Trans trans;
CarteObjectEntry entry;
if ( Utils.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 ( useXML ) {
try {
OutputStream out = null;
byte[] data = null;
String logId = trans.getLogChannelId();
boolean finishedOrStopped = trans.isFinishedOrStopped();
boolean sendResultXmlWithStatus = "Y".equalsIgnoreCase( request.getParameter( SEND_RESULT ) );
boolean dontUseCache = sendResultXmlWithStatus;
if ( finishedOrStopped && ( data = cache.get( logId, startLineNr ) ) != null && !dontUseCache ) {
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( trans, startLineNr, lastLineNr );
response.setContentType( "text/xml" );
response.setCharacterEncoding( Const.XML_ENCODING );
SlaveServerTransStatus transStatus =
new SlaveServerTransStatus( transName, entry.getId(), trans.getStatus() );
transStatus.setFirstLoggingLineNr( startLineNr );
transStatus.setLastLoggingLineNr( lastLineNr );
transStatus.setLogDate( trans.getLogDate() );
for ( int i = 0; i < trans.nrSteps(); i++ ) {
StepInterface baseStep = trans.getRunThread( i );
if ( ( baseStep.isRunning() ) || baseStep.getStatus() != StepExecutionStatus.STATUS_EMPTY ) {
StepStatus stepStatus = new StepStatus( baseStep );
transStatus.getStepStatusList().add( stepStatus );
}
}
// 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 );
transStatus.setLoggingString( loggingString );
// transStatus.setLoggingUncompressedSize( logText.length() );
// Also set the result object...
//
transStatus.setResult( trans.getResult() );
// Is the transformation paused?
//
transStatus.setPaused( trans.isPaused() );
// Send the result back as XML
//
String xml = transStatus.getXML( sendResultXmlWithStatus );
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 && ( transStatus.isFinished() || transStatus.isStopped() ) && logId != null && !dontUseCache ) {
cache.put( logId, xml, startLineNr );
}
}
response.flushBuffer();
} catch ( KettleException e ) {
throw new ServletException( "Unable to get the transformation status in XML format", e );
}
} else {
PrintWriter out = response.getWriter();
int lastLineNr = KettleLogStore.getLastBufferLineNr();
int tableBorder = 0;
response.setContentType( "text/html;charset=UTF-8" );
out.println( "" );
out.println( "" );
out.println( ""
+ BaseMessages.getString( PKG, "TransStatusServlet.KettleTransStatus" ) + " " );
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, "TransStatusServlet.TopTransStatus", transName ) )
+ "" );
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( "" + Encode.forHtml( id ) + " " );
out.print( "" + Encode.forHtml( trans.getStatus() ) + " " );
String dateStr = XMLHandler.date2string( trans.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( "Step detail" );
out.println( "" );
out.print( " "
+ BaseMessages.getString( PKG, "TransStatusServlet.Stepname" ) + " "
+ BaseMessages.getString( PKG, "TransStatusServlet.CopyNr" ) + " "
+ BaseMessages.getString( PKG, "TransStatusServlet.Read" ) + " "
+ BaseMessages.getString( PKG, "TransStatusServlet.Written" ) + " "
+ BaseMessages.getString( PKG, "TransStatusServlet.Input" ) + " "
+ BaseMessages.getString( PKG, "TransStatusServlet.Output" ) + " "
+ BaseMessages.getString( PKG, "TransStatusServlet.Updated" ) + " "
+ BaseMessages.getString( PKG, "TransStatusServlet.Rejected" ) + " "
+ BaseMessages.getString( PKG, "TransStatusServlet.Errors" ) + " "
+ BaseMessages.getString( PKG, "TransStatusServlet.Active" ) + " "
+ BaseMessages.getString( PKG, "TransStatusServlet.Time" ) + " "
+ BaseMessages.getString( PKG, "TransStatusServlet.Speed" ) + " "
+ BaseMessages.getString( PKG, "TransStatusServlet.prinout" ) + " " );
boolean evenRow = true;
for ( int i = 0; i < trans.nrSteps(); i++ ) {
StepInterface step = trans.getRunThread( i );
if ( ( step.isRunning() ) || step.getStatus() != StepExecutionStatus.STATUS_EMPTY ) {
StepStatus stepStatus = new StepStatus( step );
boolean snif = false;
String htmlString = "";
if ( step.isRunning() && !step.isStopped() && !step.isPaused() ) {
snif = true;
String sniffLink =
" "
+ Encode.forHtml( stepStatus.getStepname() ) + "";
stepStatus.setStepname( sniffLink );
}
String rowClass = evenRow ? "cellTableEvenRow" : "cellTableOddRow";
String cellClass = evenRow ? "cellTableEvenRowCell" : "cellTableOddRowCell";
htmlString = "" + stepStatus.getStepname() + " "
+ "" + stepStatus.getCopy() + " "
+ "" + stepStatus.getLinesRead() + " "
+ "" + stepStatus.getLinesWritten() + " "
+ "" + stepStatus.getLinesInput() + " "
+ "" + stepStatus.getLinesOutput() + " "
+ "" + stepStatus.getLinesUpdated() + " "
+ "" + stepStatus.getLinesRejected() + " "
+ "" + stepStatus.getErrors() + " "
+ "" + stepStatus.getStatusDescription() + " "
+ "" + stepStatus.getSeconds() + " "
+ "" + stepStatus.getSpeed() + " "
+ "" + stepStatus.getPriority() + " ";
evenRow = !evenRow;
out.print( htmlString );
}
}
out.println( "
" );
out.println( "" );
out.print( "" );
out.print( "Canvas preview" );
// Get the transformation image
//
// out.print(""
// + BaseMessages.getString(PKG, "TransStatusServlet.GetTransImage") + "");
Point max = trans.getTransMeta().getMaximum();
max.x += 20;
max.y += 20;
out.print( "" );
out.print( "" );
// Put the logging below that.
out.print( "" );
out.print( "Transformation 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, "TransStatusServlet.Log.CoundNotFindSpecTrans", transName ) ) );
} else {
out.println( ""
+ Encode.forHtml( BaseMessages.getString(
PKG, "TransStatusServlet.Log.CoundNotFindTrans", transName ) ) + "
" );
out.println( ""
+ BaseMessages.getString( PKG, "TransStatusServlet.BackToStatusPage" ) + "" );
}
}
}
public String toString() {
return "Trans Status Handler";
}
public String getService() {
return CONTEXT_PATH + " (" + toString() + ")";
}
public String getContextPath() {
return CONTEXT_PATH;
}
private String getLogText( Trans trans, int startLineNr, int lastLineNr ) throws KettleException {
try {
return KettleLogStore.getAppender().getBuffer(
trans.getLogChannel().getLogChannelId(), false, startLineNr, lastLineNr ).toString();
} catch ( OutOfMemoryError error ) {
throw new KettleException( "Log string is too long", error );
}
}
}