org.pentaho.platform.scheduler3.quartz.ActionAdapterQuartzJob.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.platform.scheduler3.quartz.ActionAdapterQuartzJob.java

Source

/*!
 * 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);
        }

    }

}