/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.platform.web.http.api.resources.services;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.platform.api.action.IAction;
import org.pentaho.platform.api.engine.IAuthorizationPolicy;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.api.engine.ISecurityHelper;
import org.pentaho.platform.api.repository2.unified.IUnifiedRepository;
import org.pentaho.platform.api.repository2.unified.RepositoryFile;
import org.pentaho.platform.api.repository2.unified.UnifiedRepositoryException;
import org.pentaho.platform.api.scheduler2.IBlockoutManager;
import org.pentaho.platform.api.scheduler2.IJobFilter;
import org.pentaho.platform.api.scheduler2.IJobTrigger;
import org.pentaho.platform.api.scheduler2.IScheduler;
import org.pentaho.platform.api.scheduler2.Job;
import org.pentaho.platform.api.scheduler2.Job.JobState;
import org.pentaho.platform.api.scheduler2.SchedulerException;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.engine.security.SecurityHelper;
import org.pentaho.platform.repository.RepositoryFilenameUtils;
import org.pentaho.platform.repository2.unified.webservices.RepositoryFileDto;
import org.pentaho.platform.scheduler2.blockout.BlockoutAction;
import org.pentaho.platform.scheduler2.quartz.QuartzScheduler;
import org.pentaho.platform.security.policy.rolebased.actions.AdministerSecurityAction;
import org.pentaho.platform.security.policy.rolebased.actions.SchedulerAction;
import org.pentaho.platform.util.messages.LocaleHelper;
import org.pentaho.platform.web.http.api.resources.ComplexJobTriggerProxy;
import org.pentaho.platform.web.http.api.resources.JobRequest;
import org.pentaho.platform.web.http.api.resources.JobScheduleParam;
import org.pentaho.platform.web.http.api.resources.JobScheduleRequest;
import org.pentaho.platform.web.http.api.resources.RepositoryFileStreamProvider;
import org.pentaho.platform.web.http.api.resources.SchedulerOutputPathResolver;
import org.pentaho.platform.web.http.api.resources.SchedulerResourceUtil;
import org.pentaho.platform.web.http.api.resources.SessionResource;
import org.pentaho.platform.web.http.api.resources.proxies.BlockStatusProxy;
public class SchedulerService {
protected IScheduler scheduler = PentahoSystem.get( IScheduler.class, "IScheduler2", null ); //$NON-NLS-1$
protected IAuthorizationPolicy policy;
protected IUnifiedRepository repository;
protected SessionResource sessionResource;
protected FileService fileService;
protected IBlockoutManager blockoutManager;
private static final Log logger = LogFactory.getLog( FileService.class );
public Job createJob( JobScheduleRequest scheduleRequest )
throws IOException, SchedulerException, IllegalAccessException {
// Used to determine if created by a RunInBackgroundCommand
boolean runInBackground =
scheduleRequest.getSimpleJobTrigger() == null && scheduleRequest.getComplexJobTrigger() == null
&& scheduleRequest.getCronJobTrigger() == null;
if ( !runInBackground && !getPolicy().isAllowed( SchedulerAction.NAME ) ) {
throw new SecurityException();
}
boolean hasInputFile = !StringUtils.isEmpty( scheduleRequest.getInputFile() );
RepositoryFile file = null;
if ( hasInputFile ) {
try {
file = getRepository().getFile( scheduleRequest.getInputFile() );
} catch ( UnifiedRepositoryException ure ) {
hasInputFile = false;
logger.warn( ure.getMessage(), ure );
}
}
// if we have an inputfile, generate job name based on that if the name is not passed in
if ( hasInputFile && StringUtils.isEmpty( scheduleRequest.getJobName() ) ) {
scheduleRequest.setJobName( file.getName().substring( 0, file.getName().lastIndexOf( "." ) ) ); //$NON-NLS-1$
} else if ( !StringUtils.isEmpty( scheduleRequest.getActionClass() ) ) {
String actionClass =
scheduleRequest.getActionClass().substring( scheduleRequest.getActionClass().lastIndexOf( "." ) + 1 );
scheduleRequest.setJobName( actionClass ); //$NON-NLS-1$
} else if ( !hasInputFile && StringUtils.isEmpty( scheduleRequest.getJobName() ) ) {
// just make up a name
scheduleRequest.setJobName( "" + System.currentTimeMillis() ); //$NON-NLS-1$
}
if ( hasInputFile ) {
Map<String, Serializable> metadata = getRepository().getFileMetadata( file.getId() );
if ( metadata.containsKey( "_PERM_SCHEDULABLE" ) ) {
boolean schedulable = Boolean.parseBoolean( (String) metadata.get( "_PERM_SCHEDULABLE" ) );
if ( !schedulable ) {
throw new IllegalAccessException();
}
}
}
Job job = null;
IJobTrigger jobTrigger = SchedulerResourceUtil.convertScheduleRequestToJobTrigger( scheduleRequest, scheduler );
HashMap<String, Serializable> parameterMap = new HashMap<String, Serializable>();
for ( JobScheduleParam param : scheduleRequest.getJobParameters() ) {
parameterMap.put( param.getName(), param.getValue() );
}
if ( isPdiFile( file ) ) {
parameterMap = handlePDIScheduling( file, parameterMap );
}
parameterMap.put( LocaleHelper.USER_LOCALE_PARAM, LocaleHelper.getLocale() );
if ( hasInputFile ) {
SchedulerOutputPathResolver outputPathResolver = getSchedulerOutputPathResolver( scheduleRequest );
String outputFile = outputPathResolver.resolveOutputFilePath();
String actionId =
getExtension( scheduleRequest.getInputFile() )
+ ".backgroundExecution"; //$NON-NLS-1$ //$NON-NLS-2$
job =
getScheduler().createJob( scheduleRequest.getJobName(), actionId, parameterMap, jobTrigger,
new RepositoryFileStreamProvider( scheduleRequest.getInputFile(), outputFile,
getAutoCreateUniqueFilename( scheduleRequest ) )
);
} else {
// need to locate actions from plugins if done this way too (but for now, we're just on main)
String actionClass = scheduleRequest.getActionClass();
try {
@SuppressWarnings ( "unchecked" )
Class<IAction> iaction = getAction( actionClass );
job = getScheduler().createJob( scheduleRequest.getJobName(), iaction, parameterMap, jobTrigger );
} catch ( ClassNotFoundException e ) {
throw new RuntimeException( e );
}
}
return job;
}
public Job triggerNow( String jobId ) throws SchedulerException {
Job job = getScheduler().getJob( jobId );
if ( getPolicy().isAllowed( SchedulerAction.NAME ) ) {
getScheduler().triggerNow( jobId );
} else {
if ( getSession().getName().equals( job.getUserName() ) ) {
getScheduler().triggerNow( jobId );
}
}
// udpate job state
job = getScheduler().getJob( jobId );
return job;
}
public Job getContentCleanerJob() throws SchedulerException {
IPentahoSession session = getSession();
final String principalName = session.getName(); // this authentication wasn't matching with the job user name,
// changed to get name via the current session
final Boolean canAdminister = getPolicy().isAllowed( AdministerSecurityAction.NAME );
List<Job> jobs = getScheduler().getJobs( getJobFilter( canAdminister, principalName ) );
if ( jobs.size() > 0 ) {
return jobs.get( 0 );
}
return null;
}
/**
* @param lineageId
* @return
* @throws java.io.FileNotFoundException
*/
public List<RepositoryFileDto> doGetGeneratedContentForSchedule( String lineageId ) throws FileNotFoundException {
return getFileService().searchGeneratedContent( getSessionResource().doGetCurrentUserDir(), lineageId,
QuartzScheduler.RESERVEDMAPKEY_LINEAGE_ID );
}
public Job getJob( String jobId ) throws SchedulerException {
return getScheduler().getJob( jobId );
}
public boolean isScheduleAllowed() {
return getPolicy().isAllowed( SchedulerAction.NAME );
}
public boolean isScheduleAllowed( String id ) {
Boolean canSchedule = isScheduleAllowed();
if ( canSchedule ) {
Map<String, Serializable> metadata = getRepository().getFileMetadata( id );
if ( metadata.containsKey( "_PERM_SCHEDULABLE" ) ) {
canSchedule = Boolean.parseBoolean( (String) metadata.get( "_PERM_SCHEDULABLE" ) );
}
}
return canSchedule;
}
public IJobFilter getJobFilter( boolean canAdminister, String principalName ) {
return new JobFilter( canAdminister, principalName );
}
private class JobFilter implements IJobFilter {
private boolean canAdminister;
private String principalName;
public JobFilter( boolean canAdminister, String principalName ) {
this.canAdminister = canAdminister;
this.principalName = principalName;
}
public boolean accept( Job job ) {
String actionClass = (String) job.getJobParams().get( "ActionAdapterQuartzJob-ActionClass" );
if ( canAdminister && "org.pentaho.platform.admin.GeneratedContentCleaner".equals( actionClass ) ) {
return true;
}
return principalName.equals( job.getUserName() )
&& "org.pentaho.platform.admin.GeneratedContentCleaner".equals( actionClass );
}
}
public String doGetCanSchedule() {
Boolean isAllowed = getPolicy().isAllowed( SchedulerAction.NAME );
return isAllowed ? "true" : "false"; //$NON-NLS-1$//$NON-NLS-2$
}
public String getState() throws SchedulerException {
return getScheduler().getStatus().name();
}
public String start() throws SchedulerException {
if ( getPolicy().isAllowed( SchedulerAction.NAME ) ) {
getScheduler().start();
}
return getScheduler().getStatus().name();
}
public String pause() throws SchedulerException {
if ( getPolicy().isAllowed( SchedulerAction.NAME ) ) {
getScheduler().pause();
}
return getScheduler().getStatus().name();
}
public String shutdown() throws SchedulerException {
if ( getPolicy().isAllowed( SchedulerAction.NAME ) ) {
getScheduler().shutdown();
}
return getScheduler().getStatus().name();
}
public JobState pauseJob( String jobId ) throws SchedulerException {
Job job = getJob( jobId );
if ( isScheduleAllowed() || PentahoSessionHolder.getSession().getName().equals( job.getUserName() ) ) {
getScheduler().pauseJob( jobId );
}
job = getJob( jobId );
return job.getState();
}
public JobState resumeJob( String jobId ) throws SchedulerException {
Job job = getJob( jobId );
if ( isScheduleAllowed() || PentahoSessionHolder.getSession().getName().equals( job.getUserName() ) ) {
getScheduler().resumeJob( jobId );
}
job = getJob( jobId );
return job.getState();
}
public boolean removeJob( String jobId ) throws SchedulerException {
Job job = getJob( jobId );
if ( isScheduleAllowed() || PentahoSessionHolder.getSession().getName().equals( job.getUserName() ) ) {
getScheduler().removeJob( jobId );
return true;
}
return false;
}
public Job getJobInfo( String jobId ) throws SchedulerException {
Job job = getJob( jobId );
if ( getSecurityHelper().isPentahoAdministrator( getSession() )
|| getSession().getName().equals( job.getUserName() ) ) {
for ( String key : job.getJobParams().keySet() ) {
Serializable value = job.getJobParams().get( key );
if ( value.getClass().isArray() ) {
String[] sa = ( new String[0] ).getClass().cast( value );
ArrayList<String> list = new ArrayList<String>();
for ( int i = 0; i < sa.length; i++ ) {
list.add( sa[i] );
}
job.getJobParams().put( key, list );
}
}
return job;
} else {
throw new RuntimeException( "Job not found or improper credentials for access" );
}
}
public List<Job> getBlockOutJobs() {
return getBlockoutManager().getBlockOutJobs();
}
public boolean hasBlockouts() {
List<Job> jobs = getBlockoutManager().getBlockOutJobs();
return jobs != null && jobs.size() > 0;
}
public boolean willFire( IJobTrigger trigger ) {
return getBlockoutManager().willFire( trigger );
}
public boolean shouldFireNow() {
return getBlockoutManager().shouldFireNow();
}
public Job addBlockout( JobScheduleRequest jobScheduleRequest ) throws IOException, IllegalAccessException, SchedulerException {
if ( isScheduleAllowed() ) {
jobScheduleRequest.setActionClass( BlockoutAction.class.getCanonicalName() );
jobScheduleRequest.getJobParameters().add( getJobScheduleParam( IBlockoutManager.DURATION_PARAM,
jobScheduleRequest.getDuration() ) );
jobScheduleRequest.getJobParameters().add( getJobScheduleParam( IBlockoutManager.TIME_ZONE_PARAM, jobScheduleRequest.getTimeZone() ) );
updateStartDateForTimeZone( jobScheduleRequest );
return createJob( jobScheduleRequest );
}
throw new IllegalAccessException();
}
protected JobScheduleParam getJobScheduleParam( String name, String value ) {
return new JobScheduleParam( name, value );
}
protected JobScheduleParam getJobScheduleParam( String name, long value ) {
return new JobScheduleParam( name, value );
}
protected void updateStartDateForTimeZone( JobScheduleRequest jobScheduleRequest ) {
SchedulerResourceUtil.updateStartDateForTimeZone( jobScheduleRequest );
}
public Job updateBlockout( String jobId, JobScheduleRequest jobScheduleRequest )
throws IllegalAccessException, SchedulerException, IOException {
if ( isScheduleAllowed() ) {
boolean isJobRemoved = removeJob( jobId );
if ( isJobRemoved ) {
Job job = addBlockout( jobScheduleRequest );
return job;
}
}
throw new IllegalArgumentException();
}
public BlockStatusProxy getBlockStatus( JobScheduleRequest jobScheduleRequest ) throws SchedulerException {
IJobTrigger trigger = convertScheduleRequestToJobTrigger( jobScheduleRequest );
Boolean totallyBlocked = false;
Boolean partiallyBlocked = getBlockoutManager().isPartiallyBlocked( trigger );
if ( partiallyBlocked ) {
totallyBlocked = !getBlockoutManager().willFire( trigger );
}
return getBlockStatusProxy( totallyBlocked, partiallyBlocked );
}
protected BlockStatusProxy getBlockStatusProxy( Boolean totallyBlocked, Boolean partiallyBlocked ) {
return new BlockStatusProxy( totallyBlocked, partiallyBlocked );
}
protected IJobTrigger convertScheduleRequestToJobTrigger( JobScheduleRequest jobScheduleRequest )
throws SchedulerException {
return SchedulerResourceUtil.convertScheduleRequestToJobTrigger( jobScheduleRequest, scheduler );
}
public JobScheduleRequest getJobInfo() {
JobScheduleRequest jobRequest = new JobScheduleRequest();
ComplexJobTriggerProxy proxyTrigger = new ComplexJobTriggerProxy();
proxyTrigger.setDaysOfMonth( new int[]{1, 2, 3} );
proxyTrigger.setDaysOfWeek( new int[]{1, 2, 3} );
proxyTrigger.setMonthsOfYear( new int[]{1, 2, 3} );
proxyTrigger.setYears( new int[]{2012, 2013} );
proxyTrigger.setStartTime( new Date() );
jobRequest.setComplexJobTrigger( proxyTrigger );
jobRequest.setInputFile( "aaaaa" );
jobRequest.setOutputFile( "bbbbb" );
ArrayList<JobScheduleParam> jobParams = new ArrayList<JobScheduleParam>();
jobParams.add( new JobScheduleParam( "param1", "aString" ) );
jobParams.add( new JobScheduleParam( "param2", 1 ) );
jobParams.add( new JobScheduleParam( "param3", true ) );
jobParams.add( new JobScheduleParam( "param4", new Date() ) );
jobRequest.setJobParameters( jobParams );
return jobRequest;
}
public JobState getJobState( JobRequest jobRequest ) throws SchedulerException {
Job job = getJob( jobRequest.getJobId() );
if ( isScheduleAllowed() || getSession().getName().equals( job.getUserName() ) ) {
return job.getState();
}
throw new UnsupportedOperationException();
}
protected IPentahoSession getSession() {
return PentahoSessionHolder.getSession();
}
public Class<IAction> getAction( String actionClass ) throws ClassNotFoundException {
return ( (Class<IAction>) Class.forName( actionClass ) );
}
public IUnifiedRepository getRepository() {
if ( repository == null ) {
repository = PentahoSystem.get( IUnifiedRepository.class );
}
return repository;
}
public IScheduler getScheduler() {
if ( scheduler == null ) {
scheduler = PentahoSystem.get( IScheduler.class, "IScheduler2", null );
}
return scheduler;
}
public IAuthorizationPolicy getPolicy() {
if ( policy == null ) {
policy = PentahoSystem.get( IAuthorizationPolicy.class );
}
return policy;
}
protected SchedulerOutputPathResolver getSchedulerOutputPathResolver( JobScheduleRequest scheduleRequest ) {
return new SchedulerOutputPathResolver( scheduleRequest );
}
protected boolean isPdiFile( RepositoryFile file ) {
return SchedulerResourceUtil.isPdiFile( file );
}
protected HashMap<String, Serializable> handlePDIScheduling( RepositoryFile file,
HashMap<String, Serializable> parameterMap ) {
return SchedulerResourceUtil.handlePDIScheduling( file, parameterMap );
}
public boolean getAutoCreateUniqueFilename( final JobScheduleRequest scheduleRequest ) {
ArrayList<JobScheduleParam> jobParameters = scheduleRequest.getJobParameters();
for ( JobScheduleParam jobParameter : jobParameters ) {
if ( "autoCreateUniqueFilename".equals( jobParameter.getName() ) && "boolean".equals( jobParameter.getType() ) ) {
return (Boolean) jobParameter.getValue();
}
}
return true;
}
public List<Job> getJobs() throws SchedulerException {
IPentahoSession session = getSession();
final String principalName = session.getName(); // this authentication wasn't matching with the job user name,
// changed to get name via the current session
final Boolean canAdminister = canAdminister( session );
List<Job> jobs = getScheduler().getJobs( new IJobFilter() {
public boolean accept( Job job ) {
if ( canAdminister ) {
return !IBlockoutManager.BLOCK_OUT_JOB_NAME.equals( job.getJobName() );
}
return principalName.equals( job.getUserName() );
}
} );
return jobs;
}
protected Boolean canAdminister( IPentahoSession session ) {
if ( getPolicy().isAllowed( AdministerSecurityAction.NAME ) ) {
return true;
}
return false;
}
protected String getExtension( String filename ) {
return RepositoryFilenameUtils.getExtension( filename );
}
/**
* Gets an instance of SessionResource
*
* @return <code>SessionResource</code>
*/
protected SessionResource getSessionResource() {
if ( sessionResource == null ) {
sessionResource = new SessionResource();
}
return sessionResource;
}
protected FileService getFileService() {
if ( fileService == null ) {
fileService = new FileService();
}
return fileService;
}
protected IBlockoutManager getBlockoutManager() {
if ( blockoutManager == null ) {
blockoutManager = PentahoSystem.get( IBlockoutManager.class, "IBlockoutManager", null ); //$NON-NLS-1$;
}
return blockoutManager;
}
protected ISecurityHelper getSecurityHelper() {
return SecurityHelper.getInstance();
}
}