// remove the scheduling infrastructure properties
params.remove( QuartzScheduler.RESERVEDMAPKEY_ACTIONCLASS );
params.remove( QuartzScheduler.RESERVEDMAPKEY_ACTIONID );
params.remove( QuartzScheduler.RESERVEDMAPKEY_ACTIONUSER );
final IBackgroundExecutionStreamProvider streamProvider =
(IBackgroundExecutionStreamProvider) params.get( QuartzScheduler.RESERVEDMAPKEY_STREAMPROVIDER );
params.remove( QuartzScheduler.RESERVEDMAPKEY_STREAMPROVIDER );
params.remove( QuartzScheduler.RESERVEDMAPKEY_UIPASSPARAM );
// The scheduled_fire_time is useful only to the blockoutAction see PDI-10171
if ( actionBean instanceof BlockoutAction ) {
params.put( IBlockoutManager.SCHEDULED_FIRE_TIME, context.getScheduledFireTime() );
}
if ( log.isDebugEnabled() ) {
log.debug( MessageFormat.format(
"Scheduling system invoking action {0} as user {1} with params [ {2} ]", actionBean //$NON-NLS-1$
.getClass().getName(), actionUser, QuartzScheduler.prettyPrintMap( params ) ) );
}
Callable<Boolean> actionBeanRunner = new Callable<Boolean>() {
public Boolean call() throws Exception {
LocaleHelper.setLocaleOverride( (Locale) params.get( LocaleHelper.USER_LOCALE_PARAM ) );
// sync job params to the action bean
ActionHarness actionHarness = new ActionHarness( actionBean );
boolean updateJob = false;
final Map<String, Object> actionParams = new HashMap<String, Object>();
actionParams.putAll( params );
if ( streamProvider != null ) {
actionParams.put( "inputStream", streamProvider.getInputStream() );
}
actionHarness.setValues( actionParams, new ActionSequenceCompatibilityFormatter() );
if ( actionBean instanceof IVarArgsAction ) {
actionParams.remove( "inputStream" );
actionParams.remove( "outputStream" );
( (IVarArgsAction) actionBean ).setVarArgs( actionParams );
}
boolean waitForFileCreated = false;
OutputStream stream = null;
if ( streamProvider != null ) {
actionParams.remove( "inputStream" );
if ( actionBean instanceof IStreamingAction ) {
streamProvider.setStreamingAction( (IStreamingAction) actionBean );
}
// BISERVER-9414 - validate that output path still exist
SchedulerOutputPathResolver resolver =
new SchedulerOutputPathResolver( streamProvider.getOutputPath(), actionUser );
String outputPath = resolver.resolveOutputFilePath();
actionParams.put( "useJcr", Boolean.TRUE );
actionParams.put( "jcrOutputPath", outputPath.substring( 0, outputPath.lastIndexOf( "/" ) ) );
if ( !outputPath.equals( streamProvider.getOutputPath() ) ) {
streamProvider.setOutputFilePath( outputPath ); // set fallback path
updateJob = true; // job needs to be deleted and recreated with the new output path
}
stream = streamProvider.getOutputStream();
if ( stream instanceof ISourcesStreamEvents ) {
( (ISourcesStreamEvents) stream ).addListener( new IStreamListener() {
public void fileCreated( final String filePath ) {
synchronized ( lock ) {
outputFilePath = filePath;
lock.notifyAll();
}
}
} );
waitForFileCreated = true;
}
actionParams.put( "outputStream", stream );
// The lineage_id is only useful for the metadata and not needed at this level see PDI-10171
actionParams.remove( QuartzScheduler.RESERVEDMAPKEY_LINEAGE_ID );
actionHarness.setValues( actionParams );
}
actionBean.execute();
if (stream != null) {
IOUtils.closeQuietly( stream );
}
if ( waitForFileCreated ) {
synchronized ( lock ) {
if ( outputFilePath == null ) {
lock.wait();
}
}
sendEmail( actionParams, params, outputFilePath );
}
return updateJob;
}
};
boolean requiresUpdate = false;
if ( ( actionUser == null ) || ( actionUser.equals( "system session" ) ) ) { //$NON-NLS-1$
// For now, don't try to run quartz jobs as authenticated if the user
// that created the job is a system user. See PPP-2350
requiresUpdate = SecurityHelper.getInstance().runAsAnonymous( actionBeanRunner );
} else {
try {
requiresUpdate = SecurityHelper.getInstance().runAsUser( actionUser, actionBeanRunner );
} catch ( Throwable t ) {
Object restartFlag = jobParams.get( QuartzScheduler.RESERVEDMAPKEY_RESTART_FLAG );
if ( restartFlag == null ) {
final SimpleJobTrigger trigger = new SimpleJobTrigger( new Date(), null, 0, 0 );
final Class<IAction> iaction = (Class<IAction>) actionBean.getClass();
// recreate the job in the context of the original creator
SecurityHelper.getInstance().runAsUser( actionUser, new Callable<Void>() {
@Override
public Void call() throws Exception {
if(streamProvider != null) {
streamProvider.setStreamingAction( null ); // remove generated content
}
QuartzJobKey jobKey = QuartzJobKey.parse( context.getJobDetail().getName() );
String jobName = jobKey.getJobName();
jobParams.put( QuartzScheduler.RESERVEDMAPKEY_RESTART_FLAG, Boolean.TRUE );
scheduler.createJob( jobName, iaction, jobParams, trigger, streamProvider );
log.warn( "New RunOnce job created for " + jobName + " -> possible startup synchronization error" );
return null;
}
} );
} else {
log.warn( "RunOnce already created, skipping" );
throw new Exception( t );
}
}
}
scheduler.fireJobCompleted( actionBean, actionUser, params, streamProvider );
if ( requiresUpdate ) {
log.warn( "Output path for job: " + context.getJobDetail().getName() + " has changed. Job requires update" );
try {
final IJobTrigger trigger = scheduler.getJob( context.getJobDetail().getName() ).getJobTrigger();
final Class<IAction> iaction = (Class<IAction>) actionBean.getClass();
// remove job with outdated/invalid output path
scheduler.removeJob( context.getJobDetail().getName() );
// recreate the job in the context of the original creator
SecurityHelper.getInstance().runAsUser( actionUser, new Callable<Void>() {
@Override
public Void call() throws Exception {
streamProvider.setStreamingAction( null ); // remove generated content
QuartzJobKey jobKey = QuartzJobKey.parse( context.getJobDetail().getName() );
String jobName = jobKey.getJobName();
org.pentaho.platform.api.scheduler2.Job j =
scheduler.createJob( jobName, iaction, jobParams, trigger, streamProvider );
log.warn( "New Job: " + j.getJobId() + " created" );