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

org.pentaho.di.job.Job Maven / Gradle / Ivy

The newest version!
//CHECKSTYLE:FileLength:OFF
/*! ******************************************************************************
 *
 * 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.job;

import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.vfs2.FileName;
import org.apache.commons.vfs2.FileObject;
import org.pentaho.di.cluster.SlaveServer;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.ExecutorInterface;
import org.pentaho.di.core.ExtensionDataInterface;
import org.pentaho.di.core.KettleEnvironment;
import org.pentaho.di.core.Result;
import org.pentaho.di.core.RowMetaAndData;
import org.pentaho.di.core.database.Database;
import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.di.core.exception.KettleDatabaseException;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettleJobException;
import org.pentaho.di.core.exception.KettleValueException;
import org.pentaho.di.core.extension.ExtensionPointHandler;
import org.pentaho.di.core.extension.KettleExtensionPoint;
import org.pentaho.di.core.gui.JobTracker;
import org.pentaho.di.core.logging.ChannelLogTable;
import org.pentaho.di.core.logging.DefaultLogLevel;
import org.pentaho.di.core.logging.HasLogChannelInterface;
import org.pentaho.di.core.logging.JobEntryLogTable;
import org.pentaho.di.core.logging.JobLogTable;
import org.pentaho.di.core.logging.KettleLogStore;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.core.logging.LogChannelInterface;
import org.pentaho.di.core.logging.LogLevel;
import org.pentaho.di.core.logging.LogStatus;
import org.pentaho.di.core.logging.LoggingBuffer;
import org.pentaho.di.core.logging.LoggingHierarchy;
import org.pentaho.di.core.logging.LoggingObjectInterface;
import org.pentaho.di.core.logging.LoggingObjectType;
import org.pentaho.di.core.logging.LoggingRegistry;
import org.pentaho.di.core.logging.Metrics;
import org.pentaho.di.core.parameters.DuplicateParamException;
import org.pentaho.di.core.parameters.NamedParams;
import org.pentaho.di.core.parameters.NamedParamsDefault;
import org.pentaho.di.core.parameters.UnknownParamException;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.value.ValueMetaString;
import org.pentaho.di.core.util.EnvUtil;
import org.pentaho.di.core.variables.VariableSpace;
import org.pentaho.di.core.variables.Variables;
import org.pentaho.di.core.vfs.KettleVFS;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.job.entries.job.JobEntryJob;
import org.pentaho.di.job.entries.special.JobEntrySpecial;
import org.pentaho.di.job.entries.trans.JobEntryTrans;
import org.pentaho.di.job.entry.JobEntryCopy;
import org.pentaho.di.job.entry.JobEntryInterface;
import org.pentaho.di.repository.ObjectId;
import org.pentaho.di.repository.ObjectRevision;
import org.pentaho.di.repository.Repository;
import org.pentaho.di.repository.RepositoryDirectoryInterface;
import org.pentaho.di.resource.ResourceUtil;
import org.pentaho.di.resource.TopLevelResource;
import org.pentaho.di.trans.Trans;
import org.pentaho.di.www.RegisterJobServlet;
import org.pentaho.di.www.RegisterPackageServlet;
import org.pentaho.di.www.SocketRepository;
import org.pentaho.di.www.StartJobServlet;
import org.pentaho.di.www.WebResult;
import org.pentaho.metastore.api.IMetaStore;

/**
 * This class executes a job as defined by a JobMeta object.
 * 

* The definition of a PDI job is represented by a JobMeta object. It is typically loaded from a .kjb file, a PDI * repository, or it is generated dynamically. The declared parameters of the job definition are then queried using * listParameters() and assigned values using calls to setParameterValue(..). * * @author Matt Casters * @since 07-apr-2003 * */ public class Job extends Thread implements VariableSpace, NamedParams, HasLogChannelInterface, LoggingObjectInterface, ExecutorInterface, ExtensionDataInterface { private static Class PKG = Job.class; // for i18n purposes, needed by Translator2!! public static final String CONFIGURATION_IN_EXPORT_FILENAME = "__job_execution_configuration__.xml"; private LogChannelInterface log; private LogLevel logLevel = DefaultLogLevel.getLogLevel(); private String containerObjectId; private JobMeta jobMeta; private int logCommitSize = 10; private Repository rep; private AtomicInteger errors; private VariableSpace variables = new Variables(); /** * The job that's launching this (sub-) job. This gives us access to the whole chain, including the parent variables, * etc. */ protected Job parentJob; /** The parent transformation */ protected Trans parentTrans; /** The parent logging interface to reference */ private LoggingObjectInterface parentLoggingObject; /** Keep a list of the job entries that were executed. org.pentaho.di.core.logging.CentralLogStore.getInstance() */ private JobTracker jobTracker; /** A flat list of results in THIS job, in the order of execution of job entries */ private final LinkedList jobEntryResults = new LinkedList(); private Date startDate, endDate, currentDate, logDate, depDate; private long batchId; /** * This is the batch ID that is passed from job to job to transformation, if nothing is passed, it's the job's batch * id */ private long passedBatchId; /** * The rows that were passed onto this job by a previous transformation. These rows are passed onto the first job * entry in this job (on the result object) */ private List sourceRows; /** The result of the job, after execution. */ private Result result; private boolean interactive; private List jobListeners; private List jobEntryListeners; private List delegationListeners; private Map activeJobEntryTransformations; private Map activeJobEntryJobs; /** Parameters of the job. */ private NamedParams namedParams = new NamedParamsDefault(); private SocketRepository socketRepository; private int maxJobEntriesLogged; private JobEntryCopy startJobEntryCopy; private Result startJobEntryResult; private String executingServer; private String executingUser; private String transactionId; private Map extensionDataMap; /** The command line arguments for the job. */ protected String[] arguments; /** Int value for storage job statuses */ private AtomicInteger status; /** *

* This enum stores bit masks which are used to manipulate with statuses over field {@link Job#status} */ enum BitMaskStatus { ACTIVE( 1 ), INITIALIZED( 2 ), STOPPED( 4 ), FINISHED( 8 ); private final int mask; // the sum of status masks public static final int BIT_STATUS_SUM = 63; BitMaskStatus( int mask ) { this.mask = mask; } } public Job( String name, String file, String[] args ) { this(); jobMeta = new JobMeta(); if ( name != null ) { setName( name + " (" + super.getName() + ")" ); } jobMeta.setName( name ); jobMeta.setFilename( file ); this.arguments = args; init(); this.log = new LogChannel( this ); } public void init() { status = new AtomicInteger(); jobListeners = new ArrayList(); jobEntryListeners = new ArrayList(); delegationListeners = new ArrayList(); // these 2 maps are being modified concurrently and must be thread-safe activeJobEntryTransformations = new ConcurrentHashMap(); activeJobEntryJobs = new ConcurrentHashMap(); extensionDataMap = new HashMap(); jobTracker = new JobTracker( jobMeta ); synchronized ( jobEntryResults ) { jobEntryResults.clear(); } errors = new AtomicInteger( 0 ); batchId = -1; passedBatchId = -1; maxJobEntriesLogged = Const.toInt( EnvUtil.getSystemProperty( Const.KETTLE_MAX_JOB_ENTRIES_LOGGED ), 1000 ); result = null; startJobEntryCopy = null; startJobEntryResult = null; this.setDefaultLogCommitSize(); } private void setDefaultLogCommitSize() { String propLogCommitSize = this.getVariable( "pentaho.log.commit.size" ); if ( propLogCommitSize != null ) { // override the logCommit variable try { logCommitSize = Integer.parseInt( propLogCommitSize ); } catch ( Exception ignored ) { logCommitSize = 10; // ignore parsing error and default to 10 } } } public Job( Repository repository, JobMeta jobMeta ) { this( repository, jobMeta, null ); } public Job( Repository repository, JobMeta jobMeta, LoggingObjectInterface parentLogging ) { this.rep = repository; this.jobMeta = jobMeta; this.containerObjectId = jobMeta.getContainerObjectId(); this.parentLoggingObject = parentLogging; if ( jobMeta.getName() != null ) { setName( jobMeta.getName() + " - " + super.getName() ); } init(); jobTracker = new JobTracker( jobMeta ); this.log = new LogChannel( this, parentLogging ); this.logLevel = log.getLogLevel(); if ( this.containerObjectId == null ) { this.containerObjectId = log.getContainerObjectId(); } } public Job() { init(); this.log = new LogChannel( this ); this.logLevel = log.getLogLevel(); } /** * Gets the name property of the JobMeta property. * * @return String name for the JobMeta */ @Override public String toString() { if ( jobMeta == null || Utils.isEmpty( jobMeta.getName() ) ) { return getName(); } else { return jobMeta.getName(); } } public static final Job createJobWithNewClassLoader() throws KettleException { try { // Load the class. Class jobClass = Const.createNewClassLoader().loadClass( Job.class.getName() ); // create the class // Try to instantiate this one... Job job = (Job) jobClass.getDeclaredConstructor().newInstance(); // Done! return job; } catch ( Exception e ) { String message = BaseMessages.getString( PKG, "Job.Log.ErrorAllocatingNewJob", e.toString() ); throw new KettleException( message, e ); } } public String getJobname() { if ( jobMeta == null ) { return null; } return jobMeta.getName(); } public void setRepository( Repository rep ) { this.rep = rep; } /** * Threads main loop: called by Thread.start(); */ @Override public void run() { ExecutorService heartbeat = null; // this job's heartbeat scheduled executor try { setStopped( false ); setFinished( false ); setInitialized( true ); // Create a new variable name space as we want jobs to have their own set of variables. // initialize from parentJob or null // variables.initializeVariablesFrom( parentJob ); setInternalKettleVariables( variables ); copyParametersFrom( jobMeta ); activateParameters(); // Run the job // fireJobStartListeners(); heartbeat = startHeartbeat( getHeartbeatIntervalInSeconds() ); result = execute(); } catch ( Throwable je ) { log.logError( BaseMessages.getString( PKG, "Job.Log.ErrorExecJob", je.getMessage() ), je ); // log.logError(Const.getStackTracker(je)); // // we don't have result object because execute() threw a curve-ball. // So we create a new error object. // result = new Result(); result.setNrErrors( 1L ); result.setResult( false ); addErrors( 1 ); // This can be before actual execution emergencyWriteJobTracker( result ); setActive( false ); setFinished( true ); setStopped( false ); } finally { try { shutdownHeartbeat( heartbeat ); ExtensionPointHandler.callExtensionPoint( log, KettleExtensionPoint.JobFinish.id, this ); jobMeta.disposeEmbeddedMetastoreProvider(); log.logDebug( BaseMessages.getString( PKG, "Job.Log.DisposeEmbeddedMetastore" ) ); fireJobFinishListeners(); // release unused vfs connections KettleVFS.freeUnusedResources(); } catch ( KettleException e ) { result.setNrErrors( 1 ); result.setResult( false ); log.logError( BaseMessages.getString( PKG, "Job.Log.ErrorExecJob", e.getMessage() ), e ); emergencyWriteJobTracker( result ); } } } private void emergencyWriteJobTracker( Result res ) { JobEntryResult jerFinalResult = new JobEntryResult( res, this.getLogChannelId(), BaseMessages.getString( PKG, "Job.Comment.JobFinished" ), null, null, 0, null ); JobTracker finalTrack = new JobTracker( this.getJobMeta(), jerFinalResult ); // jobTracker is up to date too. this.jobTracker.addJobTracker( finalTrack ); } /** * Execute a job without previous results. This is a job entry point (not recursive)
*
* * @return the result of the execution * * @throws KettleException */ private Result execute() throws KettleException { try { log.snap( Metrics.METRIC_JOB_START ); setFinished( false ); setStopped( false ); KettleEnvironment.setExecutionInformation( this, rep ); log.logMinimal( BaseMessages.getString( PKG, "Job.Comment.JobStarted" ) ); ExtensionPointHandler.callExtensionPoint( log, KettleExtensionPoint.JobStart.id, this ); // Start the tracking... JobEntryResult jerStart = new JobEntryResult( null, null, BaseMessages.getString( PKG, "Job.Comment.JobStarted" ), BaseMessages .getString( PKG, "Job.Reason.Started" ), null, 0, null ); jobTracker.addJobTracker( new JobTracker( jobMeta, jerStart ) ); setActive( true ); // Where do we start? JobEntryCopy startpoint; // synchronize this to a parent job if needed. // Object syncObject = this; if ( parentJob != null ) { syncObject = parentJob; // parallel execution in a job } synchronized ( syncObject ) { beginProcessing(); } Result res = null; if ( startJobEntryCopy == null ) { startpoint = jobMeta.findJobEntry( JobMeta.STRING_SPECIAL_START, 0, false ); } else { startpoint = startJobEntryCopy; res = startJobEntryResult; } if ( startpoint == null ) { throw new KettleJobException( BaseMessages.getString( PKG, "Job.Log.CounldNotFindStartingPoint" ) ); } JobEntryResult jerEnd = null; if ( startpoint.isStart() ) { // Perform optional looping in the special Start job entry... // // long iteration = 0; boolean isFirst = true; JobEntrySpecial jes = (JobEntrySpecial) startpoint.getEntry(); while ( ( jes.isRepeat() || isFirst ) && !isStopped() ) { isFirst = false; res = execute( 0, null, startpoint, null, BaseMessages.getString( PKG, "Job.Reason.Started" ) ); // // if (iteration > 0 && (iteration % 500) == 0) { // System.out.println("other 500 iterations: " + iteration); // } // iteration++; // } jerEnd = new JobEntryResult( res, jes.getLogChannelId(), BaseMessages.getString( PKG, "Job.Comment.JobFinished" ), BaseMessages.getString( PKG, "Job.Reason.Finished" ), null, 0, null ); } else { res = execute( 0, res, startpoint, null, BaseMessages.getString( PKG, "Job.Reason.Started" ) ); jerEnd = new JobEntryResult( res, startpoint.getEntry().getLogChannel().getLogChannelId(), BaseMessages.getString( PKG, "Job.Comment.JobFinished" ), BaseMessages.getString( PKG, "Job.Reason.Finished" ), null, 0, null ); } // Save this result... jobTracker.addJobTracker( new JobTracker( jobMeta, jerEnd ) ); log.logMinimal( BaseMessages.getString( PKG, "Job.Comment.JobFinished" ) ); setActive( false ); if ( !isStopped() ) { setFinished( true ); } return res; } finally { log.snap( Metrics.METRIC_JOB_STOP ); } } /** * Execute a job with previous results passed in.
*
* Execute called by JobEntryJob: don't clear the jobEntryResults. * * @param nr * The job entry number * @param result * the result of the previous execution * @return Result of the job execution * @throws KettleJobException */ public Result execute( int nr, Result result ) throws KettleException { setFinished( false ); setActive( true ); setInitialized( true ); KettleEnvironment.setExecutionInformation( this, rep ); // Where do we start? JobEntryCopy startpoint; // Perhaps there is already a list of input rows available? if ( getSourceRows() != null ) { result.setRows( getSourceRows() ); } startpoint = jobMeta.findJobEntry( JobMeta.STRING_SPECIAL_START, 0, false ); if ( startpoint == null ) { throw new KettleJobException( BaseMessages.getString( PKG, "Job.Log.CounldNotFindStartingPoint" ) ); } JobEntrySpecial jes = (JobEntrySpecial) startpoint.getEntry(); Result res; do { res = execute( nr, result, startpoint, null, BaseMessages.getString( PKG, "Job.Reason.StartOfJobentry" ) ); setActive( false ); } while ( jes.isRepeat() && !isStopped() ); return res; } /** * Sets the finished flag. Then launch all the job listeners and call the jobFinished method for each.
* * @see JobListener#jobFinished(Job) */ public void fireJobFinishListeners() throws KettleException { synchronized ( jobListeners ) { for ( JobListener jobListener : jobListeners ) { jobListener.jobFinished( this ); } } } /** * Call all the jobStarted method for each listener.
* * @see JobListener#jobStarted(Job) */ public void fireJobStartListeners() throws KettleException { synchronized ( jobListeners ) { for ( JobListener jobListener : jobListeners ) { jobListener.jobStarted( this ); } } } /** * Execute a job entry recursively and move to the next job entry automatically.
* Uses a back-tracking algorithm.
* * @param nr * @param prev_result * @param jobEntryCopy * @param previous * @param reason * @return * @throws KettleException */ private Result execute( final int nr, Result prev_result, final JobEntryCopy jobEntryCopy, JobEntryCopy previous, String reason ) throws KettleException { Result res = null; if ( isStopped() ) { res = new Result( nr ); res.stopped = true; return res; } // if we didn't have a previous result, create one, otherwise, copy the content... // final Result newResult; Result prevResult = null; if ( prev_result != null ) { prevResult = prev_result.clone(); } else { prevResult = new Result(); } JobExecutionExtension extension = new JobExecutionExtension( this, prevResult, jobEntryCopy, true ); ExtensionPointHandler.callExtensionPoint( log, KettleExtensionPoint.JobBeforeJobEntryExecution.id, extension ); jobMeta.disposeEmbeddedMetastoreProvider(); if ( jobMeta.getMetastoreLocatorOsgi() != null ) { jobMeta.setEmbeddedMetastoreProviderKey( jobMeta.getMetastoreLocatorOsgi().setEmbeddedMetastore( jobMeta .getEmbeddedMetaStore() ) ); } if ( extension.result != null ) { prevResult = extension.result; } if ( !extension.executeEntry ) { newResult = prevResult; } else { if ( log.isDetailed() ) { log.logDetailed( "exec(" + nr + ", " + ( prev_result != null ? prev_result.getNrErrors() : 0 ) + ", " + ( jobEntryCopy != null ? jobEntryCopy.toString() : "null" ) + ")" ); } // Which entry is next? JobEntryInterface jobEntryInterface = jobEntryCopy.getEntry(); jobEntryInterface.getLogChannel().setLogLevel( logLevel ); // Track the fact that we are going to launch the next job entry... JobEntryResult jerBefore = new JobEntryResult( null, null, BaseMessages.getString( PKG, "Job.Comment.JobStarted" ), reason, jobEntryCopy .getName(), jobEntryCopy.getNr(), environmentSubstitute( jobEntryCopy.getEntry().getFilename() ) ); jobTracker.addJobTracker( new JobTracker( jobMeta, jerBefore ) ); ClassLoader cl = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader( jobEntryInterface.getClass().getClassLoader() ); // Execute this entry... JobEntryInterface cloneJei = (JobEntryInterface) jobEntryInterface.clone(); ( (VariableSpace) cloneJei ).copyVariablesFrom( this ); cloneJei.setRepository( rep ); if ( rep != null ) { cloneJei.setMetaStore( rep.getMetaStore() ); } cloneJei.setParentJob( this ); cloneJei.setParentJobMeta( this.getJobMeta() ); final long start = System.currentTimeMillis(); cloneJei.getLogChannel().logDetailed( "Starting job entry" ); for ( JobEntryListener jobEntryListener : jobEntryListeners ) { jobEntryListener.beforeExecution( this, jobEntryCopy, cloneJei ); } if ( interactive ) { if ( jobEntryCopy.isTransformation() ) { getActiveJobEntryTransformations().put( jobEntryCopy, (JobEntryTrans) cloneJei ); } if ( jobEntryCopy.isJob() ) { getActiveJobEntryJobs().put( jobEntryCopy, (JobEntryJob) cloneJei ); } } log.snap( Metrics.METRIC_JOBENTRY_START, cloneJei.toString() ); newResult = cloneJei.execute( prevResult, nr ); log.snap( Metrics.METRIC_JOBENTRY_STOP, cloneJei.toString() ); final long end = System.currentTimeMillis(); if ( interactive ) { if ( jobEntryCopy.isTransformation() ) { getActiveJobEntryTransformations().remove( jobEntryCopy ); } if ( jobEntryCopy.isJob() ) { getActiveJobEntryJobs().remove( jobEntryCopy ); } } if ( cloneJei instanceof JobEntryTrans ) { String throughput = newResult.getReadWriteThroughput( (int) ( ( end - start ) / 1000 ) ); if ( throughput != null ) { log.logMinimal( throughput ); } } for ( JobEntryListener jobEntryListener : jobEntryListeners ) { jobEntryListener.afterExecution( this, jobEntryCopy, cloneJei, newResult ); } Thread.currentThread().setContextClassLoader( cl ); addErrors( (int) newResult.getNrErrors() ); // Also capture the logging text after the execution... // LoggingBuffer loggingBuffer = KettleLogStore.getAppender(); StringBuffer logTextBuffer = loggingBuffer.getBuffer( cloneJei.getLogChannel().getLogChannelId(), false ); newResult.setLogText( logTextBuffer.toString() + newResult.getLogText() ); // Save this result as well... // JobEntryResult jerAfter = new JobEntryResult( newResult, cloneJei.getLogChannel().getLogChannelId(), BaseMessages.getString( PKG, "Job.Comment.JobFinished" ), null, jobEntryCopy.getName(), jobEntryCopy.getNr(), environmentSubstitute( jobEntryCopy.getEntry().getFilename() ) ); jobTracker.addJobTracker( new JobTracker( jobMeta, jerAfter ) ); synchronized ( jobEntryResults ) { jobEntryResults.add( jerAfter ); // Only keep the last X job entry results in memory // if ( maxJobEntriesLogged > 0 ) { while ( jobEntryResults.size() > maxJobEntriesLogged ) { // Remove the oldest. jobEntryResults.removeFirst(); } } } } extension = new JobExecutionExtension( this, prevResult, jobEntryCopy, extension.executeEntry ); ExtensionPointHandler.callExtensionPoint( log, KettleExtensionPoint.JobAfterJobEntryExecution.id, extension ); // Try all next job entries. // // Keep track of all the threads we fired in case of parallel execution... // Keep track of the results of these executions too. // final List threads = new ArrayList(); // next 2 lists is being modified concurrently so must be synchronized for this case. final Queue threadResults = new ConcurrentLinkedQueue(); final Queue threadExceptions = new ConcurrentLinkedQueue(); final List threadEntries = new ArrayList(); // Launch only those where the hop indicates true or false // int nrNext = jobMeta.findNrNextJobEntries( jobEntryCopy ); for ( int i = 0; i < nrNext && !isStopped(); i++ ) { // The next entry is... final JobEntryCopy nextEntry = jobMeta.findNextJobEntry( jobEntryCopy, i ); // See if we need to execute this... final JobHopMeta hi = jobMeta.findJobHop( jobEntryCopy, nextEntry ); // The next comment... final String nextComment; if ( hi.isUnconditional() ) { nextComment = BaseMessages.getString( PKG, "Job.Comment.FollowedUnconditional" ); } else { if ( newResult.getResult() ) { nextComment = BaseMessages.getString( PKG, "Job.Comment.FollowedSuccess" ); } else { nextComment = BaseMessages.getString( PKG, "Job.Comment.FollowedFailure" ); } } // // If the link is unconditional, execute the next job entry (entries). // If the start point was an evaluation and the link color is correct: // green or red, execute the next job entry... // if ( hi.isUnconditional() || ( jobEntryCopy.evaluates() && ( !( hi.getEvaluation() ^ newResult .getResult() ) ) ) ) { // Start this next step! if ( log.isBasic() ) { log.logBasic( BaseMessages.getString( PKG, "Job.Log.StartingEntry", nextEntry.getName() ) ); } // Pass along the previous result, perhaps the next job can use it... // However, set the number of errors back to 0 (if it should be reset) // When an evaluation is executed the errors e.g. should not be reset. if ( nextEntry.resetErrorsBeforeExecution() ) { newResult.setNrErrors( 0 ); } // Now execute! // // if (we launch in parallel, fire the execution off in a new thread... // if ( jobEntryCopy.isLaunchingInParallel() ) { threadEntries.add( nextEntry ); Runnable runnable = new Runnable() { @Override public void run() { try { Result threadResult = execute( nr + 1, newResult, nextEntry, jobEntryCopy, nextComment ); threadResults.add( threadResult ); } catch ( Throwable e ) { log.logError( Const.getStackTracker( e ) ); threadExceptions.add( new KettleException( BaseMessages.getString( PKG, "Job.Log.UnexpectedError", nextEntry.toString() ), e ) ); Result threadResult = new Result(); threadResult.setResult( false ); threadResult.setNrErrors( 1L ); threadResults.add( threadResult ); } } }; Thread thread = new Thread( runnable ); threads.add( thread ); thread.start(); if ( log.isBasic() ) { log.logBasic( BaseMessages.getString( PKG, "Job.Log.LaunchedJobEntryInParallel", nextEntry.getName() ) ); } } else { try { // Same as before: blocks until it's done // res = execute( nr + 1, newResult, nextEntry, jobEntryCopy, nextComment ); } catch ( Throwable e ) { log.logError( Const.getStackTracker( e ) ); throw new KettleException( BaseMessages.getString( PKG, "Job.Log.UnexpectedError", nextEntry.toString() ), e ); } if ( log.isBasic() ) { log.logBasic( BaseMessages.getString( PKG, "Job.Log.FinishedJobEntry", nextEntry.getName(), res.getResult() + "" ) ); } } } } // OK, if we run in parallel, we need to wait for all the job entries to // finish... // if ( jobEntryCopy.isLaunchingInParallel() ) { for ( int i = 0; i < threads.size(); i++ ) { Thread thread = threads.get( i ); JobEntryCopy nextEntry = threadEntries.get( i ); try { thread.join(); } catch ( InterruptedException e ) { log.logError( jobMeta.toString(), BaseMessages.getString( PKG, "Job.Log.UnexpectedErrorWhileWaitingForJobEntry", nextEntry.getName() ) ); threadExceptions.add( new KettleException( BaseMessages.getString( PKG, "Job.Log.UnexpectedErrorWhileWaitingForJobEntry", nextEntry.getName() ), e ) ); } } // if(log.isBasic()) log.logBasic(BaseMessages.getString(PKG, // "Job.Log.FinishedJobEntry",startpoint.getName(),res.getResult()+"")); } // Perhaps we don't have next steps?? // In this case, return the previous result. if ( res == null ) { res = prevResult; } // See if there where any errors in the parallel execution // if ( threadExceptions.size() > 0 ) { res.setResult( false ); res.setNrErrors( threadExceptions.size() ); for ( KettleException e : threadExceptions ) { log.logError( jobMeta.toString(), e.getMessage(), e ); } // Now throw the first Exception for good measure... // throw threadExceptions.poll(); } // In parallel execution, we aggregate all the results, simply add them to // the previous result... // for ( Result threadResult : threadResults ) { res.add( threadResult ); } // If there have been errors, logically, we need to set the result to // "false"... // if ( res.getNrErrors() > 0 ) { res.setResult( false ); } return res; } /** * Wait until this job has finished. */ public void waitUntilFinished() { waitUntilFinished( -1L ); } /** * Wait until this job has finished. * * @param maxMiliseconds * the maximum number of ms to wait */ public void waitUntilFinished( long maxMiliseconds ) { long time = 0L; while ( isAlive() && ( time < maxMiliseconds || maxMiliseconds <= 0 ) ) { try { Thread.sleep( 1 ); time += 1; } catch ( InterruptedException e ) { // Ignore sleep errors } } } /** * Get the number of errors that happened in the job. * * @return nr of error that have occurred during execution. During execution of a job the number can change. */ public int getErrors() { return errors.get(); } /** * Set the number of occured errors to 0. */ public void resetErrors() { errors.set( 0 ); } /** * Add a number of errors to the total number of erros that occured during execution. * * @param nrToAdd * nr of errors to add. */ public void addErrors( int nrToAdd ) { if ( nrToAdd > 0 ) { errors.addAndGet( nrToAdd ); } } /** * Handle logging at start * * @return true if it went OK. * * @throws KettleException */ public boolean beginProcessing() throws KettleException { currentDate = new Date(); logDate = new Date(); startDate = Const.MIN_DATE; endDate = currentDate; resetErrors(); final JobLogTable jobLogTable = jobMeta.getJobLogTable(); int intervalInSeconds = Const.toInt( environmentSubstitute( jobLogTable.getLogInterval() ), -1 ); if ( jobLogTable.isDefined() ) { DatabaseMeta logcon = jobMeta.getJobLogTable().getDatabaseMeta(); String schemaName = environmentSubstitute( jobMeta.getJobLogTable().getActualSchemaName() ); String tableName = environmentSubstitute( jobMeta.getJobLogTable().getActualTableName() ); String schemaAndTable = jobMeta.getJobLogTable().getDatabaseMeta().getQuotedSchemaTableCombination( schemaName, tableName ); Database ldb = new Database( this, logcon ); ldb.shareVariablesWith( this ); ldb.connect(); ldb.setCommit( logCommitSize ); try { // See if we have to add a batch id... Long id_batch = 1L; if ( jobMeta.getJobLogTable().isBatchIdUsed() ) { id_batch = logcon.getNextBatchId( ldb, schemaName, tableName, jobLogTable.getKeyField().getFieldName() ); setBatchId( id_batch.longValue() ); if ( getPassedBatchId() <= 0 ) { setPassedBatchId( id_batch.longValue() ); } } Object[] lastr = ldb.getLastLogDate( schemaAndTable, jobMeta.getName(), true, LogStatus.END ); if ( !Utils.isEmpty( lastr ) ) { Date last; try { last = ldb.getReturnRowMeta().getDate( lastr, 0 ); } catch ( KettleValueException e ) { throw new KettleJobException( BaseMessages.getString( PKG, "Job.Log.ConversionError", "" + tableName ), e ); } if ( last != null ) { startDate = last; } } depDate = currentDate; ldb.writeLogRecord( jobMeta.getJobLogTable(), LogStatus.START, this, null ); if ( !ldb.isAutoCommit() ) { ldb.commitLog( true, jobMeta.getJobLogTable() ); } ldb.disconnect(); // If we need to do periodic logging, make sure to install a timer for // this... // if ( intervalInSeconds > 0 ) { final Timer timer = new Timer( getName() + " - interval logging timer" ); TimerTask timerTask = new TimerTask() { @Override public void run() { try { endProcessing(); } catch ( Exception e ) { log.logError( BaseMessages.getString( PKG, "Job.Exception.UnableToPerformIntervalLogging" ), e ); // Also stop the show... // errors.incrementAndGet(); stopAll(); } } }; timer.schedule( timerTask, intervalInSeconds * 1000, intervalInSeconds * 1000 ); addJobListener( new JobAdapter() { @Override public void jobFinished( Job job ) { timer.cancel(); } } ); } // Add a listener at the end of the job to take of writing the final job // log record... // addJobListener( new JobAdapter() { @Override public void jobFinished( Job job ) throws KettleException { try { endProcessing(); } catch ( KettleJobException e ) { log.logError( BaseMessages.getString( PKG, "Job.Exception.UnableToWriteToLoggingTable", jobLogTable .toString() ), e ); // do not skip exception here // job is failed in case log database record is failed! throw new KettleException( e ); } } } ); } catch ( KettleDatabaseException dbe ) { addErrors( 1 ); // This is even before actual execution throw new KettleJobException( BaseMessages.getString( PKG, "Job.Log.UnableToProcessLoggingStart", "" + tableName ), dbe ); } finally { ldb.disconnect(); } } // If we need to write out the job entry logging information, do so at the end of the job: // JobEntryLogTable jobEntryLogTable = jobMeta.getJobEntryLogTable(); if ( jobEntryLogTable.isDefined() ) { addJobListener( new JobAdapter() { @Override public void jobFinished( Job job ) throws KettleException { try { writeJobEntryLogInformation(); } catch ( KettleException e ) { throw new KettleException( BaseMessages.getString( PKG, "Job.Exception.UnableToPerformJobEntryLoggingAtJobEnd" ), e ); } } } ); } // If we need to write the log channel hierarchy and lineage information, // add a listener for that too... // ChannelLogTable channelLogTable = jobMeta.getChannelLogTable(); if ( channelLogTable.isDefined() ) { addJobListener( new JobAdapter() { @Override public void jobFinished( Job job ) throws KettleException { try { writeLogChannelInformation(); } catch ( KettleException e ) { throw new KettleException( BaseMessages.getString( PKG, "Job.Exception.UnableToPerformLoggingAtTransEnd" ), e ); } } } ); } JobExecutionExtension extension = new JobExecutionExtension( this, result, null, false ); ExtensionPointHandler.callExtensionPoint( log, KettleExtensionPoint.JobBeginProcessing.id, extension ); return true; } // // Handle logging at end /** * End processing. * * @return true, if successful * @throws KettleJobException * the kettle job exception */ private boolean endProcessing() throws KettleJobException { LogStatus status; if ( !isActive() ) { if ( isStopped() ) { status = LogStatus.STOP; } else { status = LogStatus.END; } } else { status = LogStatus.RUNNING; } try { if ( errors.get() == 0 && result != null && !result.getResult() ) { errors.incrementAndGet(); } logDate = new Date(); /* * Sums errors, read, written, etc. */ JobLogTable jobLogTable = jobMeta.getJobLogTable(); if ( jobLogTable.isDefined() ) { writeLogTableInformation( jobLogTable, status ); } return true; } catch ( Exception e ) { throw new KettleJobException( e ); // In case something else goes wrong. } } /** * Writes information to Job Log table. Cleans old records, in case job is finished. */ protected void writeLogTableInformation( JobLogTable jobLogTable, LogStatus status ) throws KettleJobException, KettleDatabaseException { boolean cleanLogRecords = status.equals( LogStatus.END ); String tableName = jobLogTable.getActualTableName(); DatabaseMeta logcon = jobLogTable.getDatabaseMeta(); Database ldb = createDataBase( logcon ); ldb.shareVariablesWith( this ); try { ldb.connect(); ldb.setCommit( logCommitSize ); ldb.writeLogRecord( jobLogTable, status, this, null ); if ( cleanLogRecords ) { ldb.cleanupLogRecords( jobLogTable ); } } catch ( KettleDatabaseException dbe ) { addErrors( 1 ); throw new KettleJobException( "Unable to end processing by writing log record to table " + tableName, dbe ); } finally { if ( !ldb.isAutoCommit() ) { ldb.commitLog( true, jobLogTable ); } ldb.disconnect(); } } /** * Write log channel information. * * @throws KettleException * the kettle exception */ protected void writeLogChannelInformation() throws KettleException { Database db = null; ChannelLogTable channelLogTable = jobMeta.getChannelLogTable(); // PDI-7070: If parent job has the same channel logging info, don't duplicate log entries Job j = getParentJob(); if ( j != null ) { if ( channelLogTable.equals( j.getJobMeta().getChannelLogTable() ) ) { return; } } // end PDI-7070 try { db = new Database( this, channelLogTable.getDatabaseMeta() ); db.shareVariablesWith( this ); db.connect(); db.setCommit( logCommitSize ); List loggingHierarchyList = getLoggingHierarchy(); for ( LoggingHierarchy loggingHierarchy : loggingHierarchyList ) { db.writeLogRecord( channelLogTable, LogStatus.START, loggingHierarchy, null ); } // Also time-out the log records in here... // db.cleanupLogRecords( channelLogTable ); } catch ( Exception e ) { throw new KettleException( BaseMessages.getString( PKG, "Trans.Exception.UnableToWriteLogChannelInformationToLogTable" ), e ); } finally { if ( !db.isAutoCommit() ) { db.commit( true ); } db.disconnect(); } } /** * Write job entry log information. * * @throws KettleException * the kettle exception */ protected void writeJobEntryLogInformation() throws KettleException { Database db = null; JobEntryLogTable jobEntryLogTable = getJobMeta().getJobEntryLogTable(); try { db = createDataBase( jobEntryLogTable.getDatabaseMeta() ); db.shareVariablesWith( this ); db.connect(); db.setCommit( logCommitSize ); for ( JobEntryCopy copy : getJobMeta().getJobCopies() ) { db.writeLogRecord( jobEntryLogTable, LogStatus.START, copy, this ); } db.cleanupLogRecords( jobEntryLogTable ); } catch ( Exception e ) { throw new KettleException( BaseMessages.getString( PKG, "Job.Exception.UnableToJobEntryInformationToLogTable" ), e ); } finally { if ( !db.isAutoCommit() ) { db.commitLog( true, jobEntryLogTable ); } db.disconnect(); } } protected Database createDataBase( DatabaseMeta databaseMeta ) { return new Database( this, databaseMeta ); } public boolean isInitialized() { int exist = status.get() & BitMaskStatus.INITIALIZED.mask; return exist != 0; } protected void setInitialized( boolean initialized ) { status.updateAndGet( v -> initialized ? v | BitMaskStatus.INITIALIZED.mask : ( BitMaskStatus.BIT_STATUS_SUM ^ BitMaskStatus.INITIALIZED.mask ) & v ); } public boolean isActive() { int exist = status.get() & BitMaskStatus.ACTIVE.mask; return exist != 0; } protected void setActive( boolean active ) { status.updateAndGet( v -> active ? v | BitMaskStatus.ACTIVE.mask : ( BitMaskStatus.BIT_STATUS_SUM ^ BitMaskStatus.ACTIVE.mask ) & v ); } public boolean isStopped() { int exist = status.get() & BitMaskStatus.STOPPED.mask; return exist != 0; } /** Stop all activity by setting the stopped property to true. */ public void stopAll() { setStopped( true ); } /** Sets the stopped. */ public void setStopped( boolean stopped ) { status.updateAndGet( v -> stopped ? v | BitMaskStatus.STOPPED.mask : ( BitMaskStatus.BIT_STATUS_SUM ^ BitMaskStatus.STOPPED.mask ) & v ); } public boolean isFinished() { int exist = status.get() & BitMaskStatus.FINISHED.mask; return exist != 0; } public void setFinished( boolean finished ) { status.updateAndGet( v -> finished ? v | BitMaskStatus.FINISHED.mask : ( BitMaskStatus.BIT_STATUS_SUM ^ BitMaskStatus.FINISHED.mask ) & v ); } public Date getStartDate() { return startDate; } public Date getEndDate() { return endDate; } public Date getCurrentDate() { return currentDate; } /** * Gets the dep date. * * @return Returns the depDate */ public Date getDepDate() { return depDate; } public Date getLogDate() { return logDate; } public JobMeta getJobMeta() { return jobMeta; } public Repository getRep() { return rep; } public Thread getThread() { return this; } public JobTracker getJobTracker() { return jobTracker; } public void setJobTracker( JobTracker jobTracker ) { this.jobTracker = jobTracker; } public void setSourceRows( List sourceRows ) { this.sourceRows = sourceRows; } /** * Gets the source rows. * * @return the source rows */ public List getSourceRows() { return sourceRows; } /** * Gets the parent job. * * @return Returns the parentJob */ public Job getParentJob() { return parentJob; } /** * Sets the parent job. * * @param parentJob * The parentJob to set. */ public void setParentJob( Job parentJob ) { this.logLevel = parentJob.getLogLevel(); this.log.setLogLevel( logLevel ); this.containerObjectId = log.getContainerObjectId(); this.parentJob = parentJob; } public Result getResult() { return result; } public void setResult( Result result ) { this.result = result; } public long getBatchId() { return batchId; } public void setBatchId( long batchId ) { this.batchId = batchId; } public long getPassedBatchId() { return passedBatchId; } public void setPassedBatchId( long jobBatchId ) { this.passedBatchId = jobBatchId; } /** * Sets the internal kettle variables. * * @param var * the new internal kettle variables. */ public void setInternalKettleVariables( VariableSpace var ) { boolean hasFilename = jobMeta != null && !Utils.isEmpty( jobMeta.getFilename() ); if ( hasFilename ) { // we have a finename that's defined. try { FileObject fileObject = KettleVFS.getFileObject( jobMeta.getFilename(), this ); FileName fileName = fileObject.getName(); // The filename of the transformation variables.setVariable( Const.INTERNAL_VARIABLE_JOB_FILENAME_NAME, fileName.getBaseName() ); // The directory of the transformation FileName fileDir = fileName.getParent(); variables.setVariable( Const.INTERNAL_VARIABLE_JOB_FILENAME_DIRECTORY, fileDir.getURI() ); } catch ( Exception e ) { variables.setVariable( Const.INTERNAL_VARIABLE_JOB_FILENAME_DIRECTORY, "" ); variables.setVariable( Const.INTERNAL_VARIABLE_JOB_FILENAME_NAME, "" ); } } else { variables.setVariable( Const.INTERNAL_VARIABLE_JOB_FILENAME_DIRECTORY, "" ); variables.setVariable( Const.INTERNAL_VARIABLE_JOB_FILENAME_NAME, "" ); } boolean hasRepoDir = jobMeta.getRepositoryDirectory() != null && jobMeta.getRepository() != null; // The name of the job variables.setVariable( Const.INTERNAL_VARIABLE_JOB_NAME, Const.NVL( jobMeta.getName(), "" ) ); // The name of the directory in the repository variables.setVariable( Const.INTERNAL_VARIABLE_JOB_REPOSITORY_DIRECTORY, hasRepoDir ? jobMeta .getRepositoryDirectory().getPath() : "" ); // setup fallbacks if ( hasRepoDir ) { variables.setVariable( Const.INTERNAL_VARIABLE_JOB_FILENAME_DIRECTORY, variables.getVariable( Const.INTERNAL_VARIABLE_JOB_REPOSITORY_DIRECTORY ) ); } else { variables.setVariable( Const.INTERNAL_VARIABLE_JOB_REPOSITORY_DIRECTORY, variables.getVariable( Const.INTERNAL_VARIABLE_JOB_FILENAME_DIRECTORY ) ); } if ( hasRepoDir ) { variables.setVariable( Const.INTERNAL_VARIABLE_ENTRY_CURRENT_DIRECTORY, variables.getVariable( Const.INTERNAL_VARIABLE_JOB_REPOSITORY_DIRECTORY ) ); if ( "/".equals( variables.getVariable( Const.INTERNAL_VARIABLE_ENTRY_CURRENT_DIRECTORY ) ) ) { variables.setVariable( Const.INTERNAL_VARIABLE_ENTRY_CURRENT_DIRECTORY, "" ); } } setInternalEntryCurrentDirectory( hasFilename, hasRepoDir ); } protected void setInternalEntryCurrentDirectory( boolean hasFilename, boolean hasRepoDir ) { variables.setVariable( Const.INTERNAL_VARIABLE_ENTRY_CURRENT_DIRECTORY, variables.getVariable( hasRepoDir ? Const.INTERNAL_VARIABLE_JOB_REPOSITORY_DIRECTORY : hasFilename ? Const.INTERNAL_VARIABLE_JOB_FILENAME_DIRECTORY : Const.INTERNAL_VARIABLE_ENTRY_CURRENT_DIRECTORY ) ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.variables.VariableSpace#copyVariablesFrom(org.pentaho.di.core.variables.VariableSpace) */ @Override public void copyVariablesFrom( VariableSpace space ) { variables.copyVariablesFrom( space ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.variables.VariableSpace#environmentSubstitute(java.lang.String) */ @Override public String environmentSubstitute( String aString ) { return variables.environmentSubstitute( aString ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.variables.VariableSpace#environmentSubstitute(java.lang.String[]) */ @Override public String[] environmentSubstitute( String[] aString ) { return variables.environmentSubstitute( aString ); } @Override public String fieldSubstitute( String aString, RowMetaInterface rowMeta, Object[] rowData ) throws KettleValueException { return variables.fieldSubstitute( aString, rowMeta, rowData ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.variables.VariableSpace#getParentVariableSpace() */ @Override public VariableSpace getParentVariableSpace() { return variables.getParentVariableSpace(); } /* * (non-Javadoc) * * @see * org.pentaho.di.core.variables.VariableSpace#setParentVariableSpace(org.pentaho.di.core.variables.VariableSpace) */ @Override public void setParentVariableSpace( VariableSpace parent ) { variables.setParentVariableSpace( parent ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.variables.VariableSpace#getVariable(java.lang.String, java.lang.String) */ @Override public String getVariable( String variableName, String defaultValue ) { return variables.getVariable( variableName, defaultValue ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.variables.VariableSpace#getVariable(java.lang.String) */ @Override public String getVariable( String variableName ) { return variables.getVariable( variableName ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.variables.VariableSpace#getBooleanValueOfVariable(java.lang.String, boolean) */ @Override public boolean getBooleanValueOfVariable( String variableName, boolean defaultValue ) { if ( !Utils.isEmpty( variableName ) ) { String value = environmentSubstitute( variableName ); if ( !Utils.isEmpty( value ) ) { return ValueMetaString.convertStringToBoolean( value ); } } return defaultValue; } /* * (non-Javadoc) * * @see * org.pentaho.di.core.variables.VariableSpace#initializeVariablesFrom(org.pentaho.di.core.variables.VariableSpace) */ @Override public void initializeVariablesFrom( VariableSpace parent ) { variables.initializeVariablesFrom( parent ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.variables.VariableSpace#listVariables() */ @Override public String[] listVariables() { return variables.listVariables(); } /* * (non-Javadoc) * * @see org.pentaho.di.core.variables.VariableSpace#setVariable(java.lang.String, java.lang.String) */ @Override public void setVariable( String variableName, String variableValue ) { variables.setVariable( variableName, variableValue ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.variables.VariableSpace#shareVariablesWith(org.pentaho.di.core.variables.VariableSpace) */ @Override public void shareVariablesWith( VariableSpace space ) { variables = space; } /* * (non-Javadoc) * * @see org.pentaho.di.core.variables.VariableSpace#injectVariables(java.util.Map) */ @Override public void injectVariables( Map prop ) { variables.injectVariables( prop ); } public String getStatus() { String message; if ( isActive() ) { if ( isStopped() ) { message = Trans.STRING_HALTING; } else { message = Trans.STRING_RUNNING; } } else if ( isFinished() ) { message = Trans.STRING_FINISHED; if ( getResult().getNrErrors() > 0 ) { message += " (with errors)"; } } else if ( isStopped() ) { message = Trans.STRING_STOPPED; if ( getResult().getNrErrors() > 0 ) { message += " (with errors)"; } } else { message = Trans.STRING_WAITING; } return message; } /** * Send to slave server. * * @param jobMeta * the job meta * @param executionConfiguration * the execution configuration * @param repository * the repository * @param metaStore * the metaStore * @return the string * @throws KettleException * the kettle exception */ public static String sendToSlaveServer( JobMeta jobMeta, JobExecutionConfiguration executionConfiguration, Repository repository, IMetaStore metaStore ) throws KettleException { String carteObjectId; SlaveServer slaveServer = executionConfiguration.getRemoteServer(); if ( slaveServer == null ) { throw new KettleException( BaseMessages.getString( PKG, "Job.Log.NoSlaveServerSpecified" ) ); } if ( Utils.isEmpty( jobMeta.getName() ) ) { throw new KettleException( BaseMessages.getString( PKG, "Job.Log.UniqueJobName" ) ); } // Align logging levels between execution configuration and remote server slaveServer.getLogChannel().setLogLevel( executionConfiguration.getLogLevel() ); try { // Inject certain internal variables to make it more intuitive. // for ( String var : Const.INTERNAL_TRANS_VARIABLES ) { executionConfiguration.getVariables().put( var, jobMeta.getVariable( var ) ); } for ( String var : Const.INTERNAL_JOB_VARIABLES ) { executionConfiguration.getVariables().put( var, jobMeta.getVariable( var ) ); } if ( executionConfiguration.isPassingExport() ) { // First export the job... slaveServer.getVariable("MASTER_HOST") // FileObject tempFile = KettleVFS.createTempFile( "jobExport", ".zip", System.getProperty( "java.io.tmpdir" ), jobMeta ); TopLevelResource topLevelResource = ResourceUtil.serializeResourceExportInterface( tempFile.getName().toString(), jobMeta, jobMeta, repository, metaStore, executionConfiguration.getXML(), CONFIGURATION_IN_EXPORT_FILENAME ); // Send the zip file over to the slave server... String result = slaveServer.sendExport( topLevelResource.getArchiveName(), RegisterPackageServlet.TYPE_JOB, topLevelResource .getBaseResourceName() ); WebResult webResult = WebResult.fromXMLString( result ); if ( !webResult.getResult().equalsIgnoreCase( WebResult.STRING_OK ) ) { throw new KettleException( "There was an error passing the exported job to the remote server: " + Const.CR + webResult.getMessage() ); } carteObjectId = webResult.getId(); } else { String xml = new JobConfiguration( jobMeta, executionConfiguration ).getXML(); String reply = slaveServer.sendXML( xml, RegisterJobServlet.CONTEXT_PATH + "/?xml=Y" ); WebResult webResult = WebResult.fromXMLString( reply ); if ( !webResult.getResult().equalsIgnoreCase( WebResult.STRING_OK ) ) { throw new KettleException( "There was an error posting the job on the remote server: " + Const.CR + webResult .getMessage() ); } carteObjectId = webResult.getId(); } // Start the job // String reply = slaveServer.execService( StartJobServlet.CONTEXT_PATH + "/?name=" + URLEncoder.encode( jobMeta.getName(), "UTF-8" ) + "&xml=Y&id=" + carteObjectId ); WebResult webResult = WebResult.fromXMLString( reply ); if ( !webResult.getResult().equalsIgnoreCase( WebResult.STRING_OK ) ) { throw new KettleException( "There was an error starting the job on the remote server: " + Const.CR + webResult .getMessage() ); } return carteObjectId; } catch ( KettleException ke ) { throw ke; } catch ( Exception e ) { throw new KettleException( e ); } } public void addJobListener( JobListener jobListener ) { synchronized ( jobListeners ) { jobListeners.add( jobListener ); } } public void addJobEntryListener( JobEntryListener jobEntryListener ) { jobEntryListeners.add( jobEntryListener ); } public void removeJobListener( JobListener jobListener ) { synchronized ( jobListeners ) { jobListeners.remove( jobListener ); } } public void removeJobEntryListener( JobEntryListener jobEntryListener ) { jobEntryListeners.remove( jobEntryListener ); } public List getJobEntryListeners() { return jobEntryListeners; } public List getJobListeners() { synchronized ( jobListeners ) { return new ArrayList( jobListeners ); } } /* * (non-Javadoc) * * @see org.pentaho.di.core.parameters.NamedParams#addParameterDefinition(java.lang.String, java.lang.String, * java.lang.String) */ @Override public void addParameterDefinition( String key, String defValue, String description ) throws DuplicateParamException { namedParams.addParameterDefinition( key, defValue, description ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.parameters.NamedParams#getParameterDescription(java.lang.String) */ @Override public String getParameterDescription( String key ) throws UnknownParamException { return namedParams.getParameterDescription( key ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.parameters.NamedParams#getParameterDefault(java.lang.String) */ @Override public String getParameterDefault( String key ) throws UnknownParamException { return namedParams.getParameterDefault( key ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.parameters.NamedParams#getParameterValue(java.lang.String) */ @Override public String getParameterValue( String key ) throws UnknownParamException { return namedParams.getParameterValue( key ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.parameters.NamedParams#listParameters() */ @Override public String[] listParameters() { return namedParams.listParameters(); } /* * (non-Javadoc) * * @see org.pentaho.di.core.parameters.NamedParams#setParameterValue(java.lang.String, java.lang.String) */ @Override public void setParameterValue( String key, String value ) throws UnknownParamException { namedParams.setParameterValue( key, value ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.parameters.NamedParams#eraseParameters() */ public void eraseParameters() { namedParams.eraseParameters(); } /* * (non-Javadoc) * * @see org.pentaho.di.core.parameters.NamedParams#clearParameters() */ public void clearParameters() { namedParams.clearParameters(); } /* * (non-Javadoc) * * @see org.pentaho.di.core.parameters.NamedParams#activateParameters() */ public void activateParameters() { String[] keys = listParameters(); for ( String key : keys ) { String value; try { value = getParameterValue( key ); } catch ( UnknownParamException e ) { value = ""; } String defValue; try { defValue = getParameterDefault( key ); } catch ( UnknownParamException e ) { defValue = ""; } if ( Utils.isEmpty( value ) ) { setVariable( key, Const.NVL( defValue, "" ) ); } else { setVariable( key, Const.NVL( value, "" ) ); } } } /* * (non-Javadoc) * * @see org.pentaho.di.core.parameters.NamedParams#copyParametersFrom(org.pentaho.di.core.parameters.NamedParams) */ public void copyParametersFrom( NamedParams params ) { namedParams.copyParametersFrom( params ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.parameters.NamedParams#mergeParametersWith(org.pentaho.di.core.parameters.NamedParams, * boolean replace) */ @Override public void mergeParametersWith( NamedParams params, boolean replace ) { namedParams.mergeParametersWith( params, replace ); } /** * Sets the socket repository. * * @param socketRepository * the new socket repository */ public void setSocketRepository( SocketRepository socketRepository ) { this.socketRepository = socketRepository; } /** * Gets the socket repository. * * @return the socket repository */ public SocketRepository getSocketRepository() { return socketRepository; } /** * Gets the log channel interface. * * @return LogChannelInterface */ public LogChannelInterface getLogChannel() { return log; } /** * Gets the job name. * * @return jobName */ public String getObjectName() { return getJobname(); } /** * Always returns null for Job. * * @return null */ public String getObjectCopy() { return null; } /** * Gets the file name. * * @return the filename */ public String getFilename() { if ( jobMeta == null ) { return null; } return jobMeta.getFilename(); } /** * Gets the log channel id. * * @return the logChannelId */ public String getLogChannelId() { return log.getLogChannelId(); } /** * Gets the jobMeta's object id. * * @return ObjectId */ public ObjectId getObjectId() { if ( jobMeta == null ) { return null; } return jobMeta.getObjectId(); } /** * Gets the job meta's object revision. * * @return ObjectRevision */ public ObjectRevision getObjectRevision() { if ( jobMeta == null ) { return null; } return jobMeta.getObjectRevision(); } /** * Gets LoggingObjectType.JOB, which is always the value for Job. * * @return LoggingObjectType LoggingObjectType.JOB */ public LoggingObjectType getObjectType() { return LoggingObjectType.JOB; } /** * Gets parent logging object. * * @return parentLoggingObject */ public LoggingObjectInterface getParent() { return parentLoggingObject; } /** * Gets the job meta's repository directory interface. * * @return RepositoryDirectoryInterface */ public RepositoryDirectoryInterface getRepositoryDirectory() { if ( jobMeta == null ) { return null; } return jobMeta.getRepositoryDirectory(); } /** * Gets the logLevel. * * @return logLevel */ public LogLevel getLogLevel() { return logLevel; } /** * Sets the log level. * * @param logLevel * the new log level */ public void setLogLevel( LogLevel logLevel ) { this.logLevel = logLevel; log.setLogLevel( logLevel ); } /** * Gets the logging hierarchy. * * @return the logging hierarchy */ public List getLoggingHierarchy() { List hierarchy = new ArrayList(); List childIds = LoggingRegistry.getInstance().getLogChannelChildren( getLogChannelId() ); for ( String childId : childIds ) { LoggingObjectInterface loggingObject = LoggingRegistry.getInstance().getLoggingObject( childId ); if ( loggingObject != null ) { hierarchy.add( new LoggingHierarchy( getLogChannelId(), batchId, loggingObject ) ); } } return hierarchy; } /** * Gets the boolean value of interactive. * * @return the interactive */ public boolean isInteractive() { return interactive; } /** * Sets the value of interactive. * * @param interactive * the interactive to set */ public void setInteractive( boolean interactive ) { this.interactive = interactive; } /** * Gets the activeJobEntryTransformations. * * @return the activeJobEntryTransformations */ public Map getActiveJobEntryTransformations() { return activeJobEntryTransformations; } /** * Gets the activeJobEntryJobs. * * @return the activeJobEntryJobs */ public Map getActiveJobEntryJobs() { return activeJobEntryJobs; } /** * Gets a flat list of results in THIS job, in the order of execution of job entries. * * @return A flat list of results in THIS job, in the order of execution of job entries */ public List getJobEntryResults() { synchronized ( jobEntryResults ) { return new ArrayList( jobEntryResults ); } } /** * Gets the carteObjectId. * * @return the carteObjectId */ public String getContainerObjectId() { return containerObjectId; } /** * Sets the execution container object id (containerObjectId). * * @param containerObjectId * the execution container object id to set */ public void setContainerObjectId( String containerObjectId ) { this.containerObjectId = containerObjectId; } /** * Gets the parent logging object. * * @return the parent logging object */ public LoggingObjectInterface getParentLoggingObject() { return parentLoggingObject; } /** * Gets the registration date. For job, this always returns null * * @return null */ public Date getRegistrationDate() { return null; } /** * Gets the start job entry copy. * * @return the startJobEntryCopy */ public JobEntryCopy getStartJobEntryCopy() { return startJobEntryCopy; } /** * Sets the start job entry copy. * * @param startJobEntryCopy * the startJobEntryCopy to set */ public void setStartJobEntryCopy( JobEntryCopy startJobEntryCopy ) { this.startJobEntryCopy = startJobEntryCopy; } /** * Gets the executing server. * * @return the executingServer */ public String getExecutingServer() { if ( executingServer == null ) { setExecutingServer( Const.getHostname() ); } return executingServer; } /** * Sets the executing server. * * @param executingServer * the executingServer to set */ public void setExecutingServer( String executingServer ) { this.executingServer = executingServer; } /** * Gets the executing user. * * @return the executingUser */ public String getExecutingUser() { return executingUser; } /** * Sets the executing user. * * @param executingUser * the executingUser to set */ public void setExecutingUser( String executingUser ) { this.executingUser = executingUser; } @Override public boolean isGatheringMetrics() { return log != null && log.isGatheringMetrics(); } @Override public void setGatheringMetrics( boolean gatheringMetrics ) { if ( log != null ) { log.setGatheringMetrics( gatheringMetrics ); } } @Override public boolean isForcingSeparateLogging() { return log != null && log.isForcingSeparateLogging(); } @Override public void setForcingSeparateLogging( boolean forcingSeparateLogging ) { if ( log != null ) { log.setForcingSeparateLogging( forcingSeparateLogging ); } } /** * Gets the transaction id. * * @return the transactionId */ public String getTransactionId() { return transactionId; } /** * Sets the transaction id. * * @param transactionId * the transactionId to set */ public void setTransactionId( String transactionId ) { this.transactionId = transactionId; } public List getDelegationListeners() { return delegationListeners; } public void setDelegationListeners( List delegationListeners ) { this.delegationListeners = delegationListeners; } public void addDelegationListener( DelegationListener delegationListener ) { delegationListeners.add( delegationListener ); } public String[] getArguments() { return arguments; } public void setArguments( String[] arguments ) { this.arguments = arguments; } public Trans getParentTrans() { return parentTrans; } public void setParentTrans( Trans parentTrans ) { this.parentTrans = parentTrans; } public Map getExtensionDataMap() { return extensionDataMap; } public Result getStartJobEntryResult() { return startJobEntryResult; } public void setStartJobEntryResult( Result startJobEntryResult ) { this.startJobEntryResult = startJobEntryResult; } protected ExecutorService startHeartbeat( final long intervalInSeconds ) { final ScheduledExecutorService heartbeat = Executors.newSingleThreadScheduledExecutor( new ThreadFactory() { @Override public Thread newThread( Runnable r ) { Thread thread = new Thread( r, "Job Heartbeat Thread for: " + getName() ); thread.setDaemon( true ); return thread; } } ); heartbeat.scheduleAtFixedRate( new Runnable() { public void run() { if ( Job.this.isFinished() ) { log.logBasic( "Shutting down heartbeat signal for " + jobMeta.getName() ); shutdownHeartbeat( heartbeat ); return; } try { log.logDebug( "Triggering heartbeat signal for " + jobMeta.getName() + " at every " + intervalInSeconds + " seconds" ); ExtensionPointHandler.callExtensionPoint( log, KettleExtensionPoint.JobHeartbeat.id, Job.this ); } catch ( KettleException e ) { log.logError( e.getMessage(), e ); } } }, intervalInSeconds /* initial delay */, intervalInSeconds /* interval delay */, TimeUnit.SECONDS ); return heartbeat; } protected void shutdownHeartbeat( ExecutorService heartbeat ) { if ( heartbeat != null ) { try { heartbeat.shutdownNow(); // prevents waiting tasks from starting and attempts to stop currently executing ones } catch ( Throwable t ) { /* do nothing */ } } } private int getHeartbeatIntervalInSeconds() { JobMeta meta = this.jobMeta; // 1 - check if there's a user defined value ( job-specific ) heartbeat periodic interval; // 2 - check if there's a default defined value ( job-specific ) heartbeat periodic interval; // 3 - use default Const.HEARTBEAT_PERIODIC_INTERVAL_IN_SECS if none of the above have been set try { if ( meta != null ) { return Const.toInt( meta.getParameterValue( Const.VARIABLE_HEARTBEAT_PERIODIC_INTERVAL_SECS ), Const.toInt( meta .getParameterDefault( Const.VARIABLE_HEARTBEAT_PERIODIC_INTERVAL_SECS ), Const.HEARTBEAT_PERIODIC_INTERVAL_IN_SECS ) ); } } catch ( Exception e ) { /* do nothing, return Const.HEARTBEAT_PERIODIC_INTERVAL_IN_SECS */ } return Const.HEARTBEAT_PERIODIC_INTERVAL_IN_SECS; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy