net.urlgrey.mythpodcaster.transcode.GlobalTranscodeAndCleanupTaskImpl.java Source code

Java tutorial

Introduction

Here is the source code for net.urlgrey.mythpodcaster.transcode.GlobalTranscodeAndCleanupTaskImpl.java

Source

/*
 * GlobalTranscodeAndCleanupTaskImpl.java
 * 
 * Created: Oct 8, 2009 12:41:45 PM
 * 
 * Copyright (C) 2009 Scott Kidder
 * 
 * This file is part of MythPodcaster
 * 
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * 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
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this program. If
 * not, see <http://www.gnu.org/licenses/>.
 */
package net.urlgrey.mythpodcaster.transcode;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import net.urlgrey.mythpodcaster.dao.SubscriptionsDAO;
import net.urlgrey.mythpodcaster.jobs.StatusBean;
import net.urlgrey.mythpodcaster.jobs.StatusBean.StatusMode;
import net.urlgrey.mythpodcaster.xml.FeedSubscriptionItem;

import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import com.sun.syndication.feed.synd.SyndFeed;

/**
 * @author scott
 * 
 */
public class GlobalTranscodeAndCleanupTaskImpl implements ApplicationContextAware {

    private static final Logger LOGGER = Logger.getLogger(GlobalTranscodeAndCleanupTaskImpl.class);
    private static final long QUEUE_POLLING_FREQUENCY = 30000;
    private SubscriptionsDAO subscriptionsDao;
    private FeedFileAccessor feedFileAccessor;
    private ThreadPoolTaskExecutor executor;
    private ApplicationContext applicationContext;
    private StatusBean status;

    public void execute() {
        LOGGER.debug("Job process starting");

        try {
            doWork();
        } catch (Exception e) {
            LOGGER.error("Error occurred during job", e);
        }
        LOGGER.debug("Job process finished");
    }

    private void doWork() {
        status.setMode(StatusMode.TRANSCODING);
        status.setCurrentTriggerStart(new Date());
        status.setCurrentTranscodeStart(null);
        status.clearDisplayFields();

        final List<FeedSubscriptionItem> purgeList = new ArrayList<FeedSubscriptionItem>();

        // retrieve series subscriptions
        List<FeedSubscriptionItem> subscriptions = subscriptionsDao.findSubscriptions();

        // iterate over each series
        for (FeedSubscriptionItem subscription : subscriptions) {
            // parse the XML-encoded RSS Feed into a Java object
            final SyndFeed feed;
            try {
                feed = feedFileAccessor.readFeed(subscription.getSeriesId(), subscription.getTranscodeProfile(),
                        subscription.getTitle());
            } catch (IOException e) {
                LOGGER.error("Continuing, error reading feed for recordId[" + subscription.getSeriesId() + "]", e);
                continue;
            }

            if (subscription.isActive() == false) {
                // if the series is inactive, then delete the feed, it's transcoded files, and the
                // subscription entry
                feedFileAccessor.purgeFeed(subscription.getSeriesId(), subscription.getTranscodeProfile(), feed);
                purgeList.add(subscription);
            }
        }

        // purge those subscriptions marked for deletion
        subscriptionsDao.purge(purgeList);

        // refresh the list of subscriptions
        subscriptions = subscriptionsDao.findSubscriptions();

        // iterate over each series to identify those requiring transcoding
        for (FeedSubscriptionItem subscription : subscriptions) {
            LOGGER.debug("Processing feed subscription for recordId[" + subscription.getSeriesId() + "]");

            // parse the XML-encoded RSS Feed into a Java object
            final SyndFeed feed;
            try {
                feed = feedFileAccessor.readFeed(subscription.getSeriesId(), subscription.getTranscodeProfile(),
                        subscription.getTitle());
            } catch (IOException e) {
                LOGGER.error("Continuing, error reading feed for recordId[" + subscription.getSeriesId() + "]", e);
                continue;
            }

            Runnable task = (Runnable) this.applicationContext.getBean("feedTranscodingTask",
                    new Object[] { subscription, feed });
            executor.execute(task);
        }

        // add a task to the execution queue that denotes the end of the queue
        final Object semaphore = new Object();
        executor.execute(new TranscodingCompletionTaskImpl(semaphore));

        try {
            synchronized (semaphore) {
                semaphore.wait();
            }

            // check the queue and perform a manual poll if there are still tasks,
            // as in the case of parallel task execution (2 or more threads)
            while (executor.getActiveCount() > 0) {
                try {
                    Thread.sleep(QUEUE_POLLING_FREQUENCY);
                } catch (InterruptedException e) {
                    LOGGER.warn("Thread was interrupted while polling for thread-pool to finish work");
                }
            }
        } catch (InterruptedException e) {
            LOGGER.warn("Thread was interrupted while waiting for thread-pool to finish work");
        }

        status.setMode(StatusMode.IDLE);
        status.setCurrentTriggerStart(null);
        status.setCurrentTranscodeStart(null);
        status.clearDisplayFields();
    }

    private class TranscodingCompletionTaskImpl implements Runnable {

        private Object semaphore;

        public TranscodingCompletionTaskImpl(Object semaphore) {
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            synchronized (this.semaphore) {
                this.semaphore.notify();
            }
        }

    }

    @Required
    public void setStatus(StatusBean status) {
        this.status = status;
    }

    @Required
    public void setSubscriptionsDao(SubscriptionsDAO subscriptionsDao) {
        this.subscriptionsDao = subscriptionsDao;
    }

    @Required
    public void setFeedFileAccessor(FeedFileAccessor feedAccessor) {
        this.feedFileAccessor = feedAccessor;
    }

    @Required
    public void setExecutor(ThreadPoolTaskExecutor executor) {
        this.executor = executor;
    }

    @Required
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}