Java tutorial
/*! * 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.scheduler3.quartz; import java.io.OutputStream; import java.io.Serializable; import java.text.MessageFormat; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.concurrent.Callable; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.osgi.service.log.LogService; import org.pentaho.platform.api.action.IAction; import org.pentaho.platform.api.action.IActionPluginManager; import org.pentaho.platform.api.action.IStreamingAction; import org.pentaho.platform.api.action.IVarArgsAction; import org.pentaho.platform.api.repository2.unified.ISourcesStreamEvents; import org.pentaho.platform.api.repository2.unified.IStreamListener; import org.pentaho.platform.api.repository2.unified.IUnifiedRepository; import org.pentaho.platform.api.repository2.unified.RepositoryFile; import org.pentaho.platform.api.repository2.unified.data.simple.SimpleRepositoryFileData; import org.pentaho.platform.api.scheduler3.IBackgroundExecutionStreamProvider; import org.pentaho.platform.api.scheduler3.IBlockoutManager; import org.pentaho.platform.api.scheduler3.IJobTrigger; import org.pentaho.platform.api.scheduler3.IScheduler; import org.pentaho.platform.api.scheduler3.SimpleJobTrigger; import org.pentaho.platform.engine.services.solution.ActionSequenceCompatibilityFormatter; import org.pentaho.platform.scheduler3.blockout.BlockoutAction; import org.pentaho.platform.scheduler3.messsages.Messages; import org.pentaho.platform.util.beans.ActionHarness; import org.pentaho.platform.util.messages.LocaleHelper; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; /** * A Quartz job that is responsible for executing the {@link IAction} referred * to in the job context. * * @author aphillips */ public class ActionAdapterQuartzJob extends OSGiJobSupport { private static final long RETRY_COUNT = 6; private static final long RETRY_SLEEP_AMOUNT = 10000; private String outputFilePath = null; private Object lock = new Object(); private volatile IScheduler scheduler; protected IAction resolveAction(IActionPluginManager pluginManager, JobDataMap jobDataMap) throws JobExecutionException { String actionClass = jobDataMap.getString(OSGiQuartzSchedulerV2.RESERVEDMAPKEY_ACTIONCLASS); String actionId = jobDataMap.getString(OSGiQuartzSchedulerV2.RESERVEDMAPKEY_ACTIONID); if (StringUtils.isEmpty(actionId) && StringUtils.isEmpty(actionClass)) { throw new LoggingJobExecutionException(Messages.getInstance().getErrorString( "ActionAdapterQuartzJob.ERROR_0001_REQUIRED_PARAM_MISSING", //$NON-NLS-1$ OSGiQuartzSchedulerV2.RESERVEDMAPKEY_ACTIONCLASS, OSGiQuartzSchedulerV2.RESERVEDMAPKEY_ACTIONID)); } IAction action = null; try { action = pluginManager.getActionPluginByClassName(actionClass).newInstance(); } catch (Exception e) { throw new JobExecutionException("Error instanciating IAction bean", e); } return action; } @SuppressWarnings("unchecked") public void execute(JobExecutionContext context) throws JobExecutionException { super.init(context); JobDataMap jobDataMap = context.getMergedJobDataMap(); String actionUser = jobDataMap.getString(OSGiQuartzSchedulerV2.RESERVEDMAPKEY_ACTIONUSER); Object bean = resolveAction(actionPluginManager, jobDataMap); if (!(bean instanceof IAction)) { throw new LoggingJobExecutionException( Messages.getInstance().getErrorString("ActionAdapterQuartzJob.ERROR_0003_ACTION_WRONG_TYPE", //$NON-NLS-1$ jobDataMap.getString(OSGiQuartzSchedulerV2.RESERVEDMAPKEY_ACTIONCLASS), IAction.class.getName())); } final IAction actionBean = (IAction) bean; try { invokeAction(actionBean, actionUser, context, GeneralUtils.toSerializableValueMap(jobDataMap.getWrappedMap())); } catch (Throwable t) { // ensure that scheduler thread isn't blocked on lock synchronized (lock) { lock.notifyAll(); } // We should not distinguish between checked and unchecked // exceptions here. All job execution failures // should result in a rethrow of a quartz exception throw new LoggingJobExecutionException(Messages.getInstance() .getErrorString("ActionAdapterQuartzJob.ERROR_0004_ACTION_FAILED", actionBean //$NON-NLS-1$ .getClass().getName()), t); } } protected void invokeAction(final IAction actionBean, final String actionUser, final JobExecutionContext context, final Map<String, Serializable> params) throws Exception { final Map<String, Serializable> jobParams = new HashMap<String, Serializable>(params); // shallow copy // remove the scheduling infrastructure properties params.remove(OSGiQuartzSchedulerV2.RESERVEDMAPKEY_ACTIONCLASS); params.remove(OSGiQuartzSchedulerV2.RESERVEDMAPKEY_ACTIONID); params.remove(OSGiQuartzSchedulerV2.RESERVEDMAPKEY_ACTIONUSER); final IBackgroundExecutionStreamProvider streamProvider = (IBackgroundExecutionStreamProvider) params .get(OSGiQuartzSchedulerV2.RESERVEDMAPKEY_STREAMPROVIDER); params.remove(OSGiQuartzSchedulerV2.RESERVEDMAPKEY_STREAMPROVIDER); params.remove(OSGiQuartzSchedulerV2.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()); } log.log(LogService.LOG_DEBUG, MessageFormat.format("Scheduling system invoking action {0} as user {1} with params [ {2} ]", //$NON-NLS-1$ actionBean.getClass().getName(), actionUser, OSGiQuartzSchedulerV2.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( * OSGiQuartzScheduler.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; } }; actionBeanRunner.call(); 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( * OSGiQuartzScheduler.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( * OSGiQuartzScheduler.RESERVEDMAPKEY_RESTART_FLAG, Boolean.TRUE ); * scheduler.createJob( jobName, iaction, jobParams, trigger, * streamProvider ); log.log(LogService.LOG_WARNING, * "New RunOnce job created for " + jobName + * " -> possible startup synchronization error" ); return null; } } ); } * else { log.log(LogService.LOG_WARNING, * "RunOnce already created, skipping" ); throw new Exception( t ); } } * } */ scheduler.fireJobCompleted(actionBean, actionUser, params, streamProvider); /* * if ( requiresUpdate ) { log.log(LogService.LOG_WARNING, * "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.log(LogService.LOG_WARNING, "New Job: " + * j.getJobId() + " created" ); return null; } } ); } catch ( Exception * e ) { log.log(LogService.LOG_ERROR, e.getMessage(), e ); } } */ log.log(LogService.LOG_DEBUG, MessageFormat .format("Scheduling system successfully invoked action {0} as user {1} with params [ {2} ]", //$NON-NLS-1$ actionBean.getClass().getName(), actionUser, OSGiQuartzSchedulerV2.prettyPrintMap(params))); } private void sendEmail(Map<String, Object> actionParams, Map<String, Serializable> params, String filePath) { /* * try { IUnifiedRepository repo = PentahoSystem.get( * IUnifiedRepository.class ); RepositoryFile sourceFile = repo.getFile( * filePath ); // add metadata Map<String, Serializable> metadata = * repo.getFileMetadata( sourceFile.getId() ); String lineageId = * (String) params.get( OSGiQuartzScheduler.RESERVEDMAPKEY_LINEAGE_ID ); * metadata.put( OSGiQuartzScheduler.RESERVEDMAPKEY_LINEAGE_ID, * lineageId ); repo.setFileMetadata( sourceFile.getId(), metadata ); // * send email SimpleRepositoryFileData data = repo.getDataForRead( * sourceFile.getId(), SimpleRepositoryFileData.class ); * * // if email is setup and we have tos, then do it Emailer emailer = * new Emailer(); if ( !emailer.setup() ) { // email not configured * return; } String to = (String) actionParams.get( "_SCH_EMAIL_TO" ); * String cc = (String) actionParams.get( "_SCH_EMAIL_CC" ); String bcc * = (String) actionParams.get( "_SCH_EMAIL_BCC" ); if ( ( to == null || * "".equals( to ) ) && ( cc == null || "".equals( cc ) ) && ( bcc == * null || "".equals( bcc ) ) ) { // no destination return; } * emailer.setTo( to ); emailer.setCc( cc ); emailer.setBcc( bcc ); * emailer.setAttachment( data.getInputStream() ); * emailer.setAttachmentName( "attachment" ); String attachmentName = * (String) actionParams.get( "_SCH_EMAIL_ATTACHMENT_NAME" ); if ( * attachmentName != null && !"".equals( attachmentName ) ) { String * path = filePath; if ( path.endsWith( ".*" ) ) { path = path.replace( * ".*", "" ); } String extension = MimeHelper.getExtension( * data.getMimeType() ); if ( extension == null ) { extension = ".bin"; * } if ( !attachmentName.endsWith( extension ) ) { * emailer.setAttachmentName( attachmentName + extension ); } else { * emailer.setAttachmentName( attachmentName ); } } else if ( data != * null ) { String path = filePath; if ( path.endsWith( ".*" ) ) { path * = path.replace( ".*", "" ); } String extension = * MimeHelper.getExtension( data.getMimeType() ); if ( extension == null * ) { extension = ".bin"; } path = path.substring( path.lastIndexOf( * "/" ) + 1, path.length() ); if ( !path.endsWith( extension ) ) { * emailer.setAttachmentName( path + extension ); } else { * emailer.setAttachmentName( path ); } } if ( data == null || * data.getMimeType() == null || "".equals( data.getMimeType() ) ) { * emailer.setAttachmentMimeType( "binary/octet-stream" ); } else { * emailer.setAttachmentMimeType( data.getMimeType() ); } String subject * = (String) actionParams.get( "_SCH_EMAIL_SUBJECT" ); if ( subject != * null && !"".equals( subject ) ) { emailer.setSubject( subject ); } * else { emailer.setSubject( "Pentaho Scheduler: " + * emailer.getAttachmentName() ); } String message = (String) * actionParams.get( "_SCH_EMAIL_MESSAGE" ); if ( subject != null && * !"".equals( subject ) ) { emailer.setBody( message ); } * emailer.send(); } catch ( Exception e ) { * log.log(LogService.LOG_WARNING, e.getMessage(), e ); } */ } class LoggingJobExecutionException extends JobExecutionException { private static final long serialVersionUID = -4124907454208034326L; public LoggingJobExecutionException(String msg) { super(msg); log.log(LogService.LOG_ERROR, msg); } public LoggingJobExecutionException(String msg, Throwable t) { super(msg, t); log.log(LogService.LOG_ERROR, msg, t); } } }