package org.springframework.social.importer; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersBuilder; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.context.Lifecycle; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler; import org.springframework.util.Assert; import java.io.File; import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * component that manages Spring Batch jobs to import photos from Flickr and * downloads them to a local cache where they can be used. * * @author Josh Long */ public class FlickrImporter implements Lifecycle { private volatile JobLauncher jobLauncher; private volatile Job importFlickrPhotosJob; private volatile Map<File, JobExecution> mapOfFilesToRunningJobs = new ConcurrentHashMap<File, JobExecution>(); private volatile TaskScheduler scheduler; public FlickrImporter(Job importFlickrPhotosJob, JobLauncher jobLauncher, TaskScheduler s) { this.importFlickrPhotosJob = importFlickrPhotosJob; this.jobLauncher = jobLauncher; this.scheduler = s; } /** * call this to kick off the import job. * * @param file the directory to which the imported photos should be written */ public void importPhotosToDirectory( String at, String atSecret, String consumerKey, String consumerSecret, File file) throws Throwable { Assert.notNull(file, "you must provide a non-null File object."); Assert.isTrue(file.exists(), "the " + file.getAbsolutePath() + " must exist."); Assert.isTrue(file.canWrite(), "we must be able to write to " + file.getAbsolutePath() + "."); JobParameters jp = new JobParametersBuilder() .addString("accessToken", at) .addString("accessTokenSecret", atSecret) .addString("consumerKey", consumerKey) .addString("consumerSecret", consumerSecret) .addString("output", file.getAbsolutePath()) .toJobParameters(); JobExecution jobExecution = jobLauncher.run(this.importFlickrPhotosJob, jp); this.mapOfFilesToRunningJobs.put(file, jobExecution); } /** * tests to see if any jobs can be removed and, if so, does. * <p/> * todo we should re-work this in terms of {@link java.lang.ref.WeakReference weak references} and {@link java.util.WeakHashMap weak hash map}. */ public static class JobCleanupRunnable implements Runnable { private volatile Map<File, JobExecution> executionMap; public JobCleanupRunnable(Map<File, JobExecution> ex) { this.executionMap = ex; } @Override public void run() { for (Map.Entry<File, JobExecution> e : executionMap.entrySet()) if (!e.getValue().isRunning()) executionMap.remove(e.getKey()); } } @Override public void start() { // we don't have a particular obligation to do anything here.. if (null == this.scheduler) { this.scheduler = new ConcurrentTaskScheduler(); } this.scheduler.scheduleAtFixedRate(new JobCleanupRunnable(this.mapOfFilesToRunningJobs), 1000); } @Override public void stop() { for (JobExecution jobExecution : this.mapOfFilesToRunningJobs.values()) { jobExecution.stop(); } } @Override public boolean isRunning() { return this.mapOfFilesToRunningJobs.size() > 0; } }