/* * JAME 6.2.1 * http://jame.sourceforge.net * * Copyright 2001, 2016 Andrea Medeghini * * This file is part of JAME. * * JAME is an application for creating fractals and other graphics artifacts. * * JAME 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. * * JAME 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 JAME. If not, see <http://www.gnu.org/licenses/>. * */ package net.sf.jame.queue; import java.awt.Image; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import net.sf.jame.core.extension.ExtensionException; import net.sf.jame.core.extension.ExtensionNotFoundException; import net.sf.jame.core.extension.ExtensionReference; import net.sf.jame.core.util.DefaultThreadFactory; import net.sf.jame.core.util.Tile; import net.sf.jame.core.util.IntegerVector2D; import net.sf.jame.core.util.Surface; import net.sf.jame.core.util.Worker; import net.sf.jame.queue.clip.ClipPreview; import net.sf.jame.queue.clip.RenderClipDataRow; import net.sf.jame.queue.job.RenderJobDataRow; import net.sf.jame.queue.profile.RenderProfileDataRow; import net.sf.jame.queue.spool.DefaultJobService; import net.sf.jame.queue.spool.JobData; import net.sf.jame.queue.spool.JobListener; import net.sf.jame.queue.spool.JobService; import net.sf.jame.queue.spool.JobServiceListener; import net.sf.jame.queue.spool.SpoolJobInterface; import net.sf.jame.queue.spool.job.CopyProcessSpoolJob; import net.sf.jame.queue.spool.job.CopyProcessSpoolJobFactory; import net.sf.jame.queue.spool.job.PostProcessSpoolJob; import net.sf.jame.queue.spool.job.PostProcessSpoolJobFactory; import net.sf.jame.twister.TwisterClip; import net.sf.jame.twister.TwisterRuntime; import net.sf.jame.twister.renderer.DefaultTwisterRenderer; /** * @author Andrea Medeghini */ public class RenderService { public static final int SERVICE_COPY_PROCESS = 0; public static final int SERVICE_POST_PROCESS = 1; public static final int SERVICE_PROCESS = 2; private final Worker spoolWorker = new Worker(new DefaultThreadFactory("Spool Worker", true, Thread.NORM_PRIORITY)); private final Worker serviceWorker = new Worker(new DefaultThreadFactory("Service Worker", true, Thread.NORM_PRIORITY)); private final Worker previewWorker = new Worker(new DefaultThreadFactory("Preview Worker", true, Thread.NORM_PRIORITY)); private final HashMap<Integer, String> postProcessJobs = new HashMap<Integer, String>(); private final HashMap<Integer, String> copyProcessJobs = new HashMap<Integer, String>(); private final HashMap<Integer, String> processJobs = new HashMap<Integer, String>(); private final HashMap<Integer, ClipPreview> previews = new HashMap<Integer, ClipPreview>(); private final List<JobServiceListener> listeners = new ArrayList<JobServiceListener>(); private JobService<? extends SpoolJobInterface> processService; private final JobService<PostProcessSpoolJob> postProcessService; private final JobService<CopyProcessSpoolJob> copyProcessService; private final DefaultJobListener jobListener = new DefaultJobListener(); private final DefaultJobServiceListener postProcessServicelistener = new DefaultJobServiceListener(SERVICE_COPY_PROCESS); private final DefaultJobServiceListener copyProcessServicelistener = new DefaultJobServiceListener(SERVICE_POST_PROCESS); private final DefaultJobServiceListener processServicelistener = new DefaultJobServiceListener(SERVICE_PROCESS); private final List<PreviewListener> previewListeners = new LinkedList<PreviewListener>(); private ExtensionReference serviceReference; private final LibraryService service; /** * @param service * @param jobService * @throws ExtensionException * @throws ExtensionNotFoundException */ public RenderService(final LibraryService service, final ExtensionReference serviceReference) throws ExtensionException { this.service = service; this.serviceReference = serviceReference; service.addServiceListener(new DefaultLibraryServiceListener()); postProcessService = new DefaultJobService<PostProcessSpoolJob>(SERVICE_POST_PROCESS, "PostProcessor", new PostProcessSpoolJobFactory(service, spoolWorker), spoolWorker); copyProcessService = new DefaultJobService<CopyProcessSpoolJob>(SERVICE_COPY_PROCESS, "CopyProcessor", new CopyProcessSpoolJobFactory(service, spoolWorker), spoolWorker); processService = RenderServiceRegistry.getInstance().getSpoolRegistry().getExtension(serviceReference.getExtensionId()).createExtensionRuntime().getJobService(SERVICE_PROCESS, service, spoolWorker); postProcessService.addServiceListener(postProcessServicelistener); copyProcessService.addServiceListener(copyProcessServicelistener); processService.addServiceListener(processServicelistener); postProcessService.start(); copyProcessService.start(); processService.start(); previewWorker.start(); serviceWorker.start(); spoolWorker.start(); } /** * @param jobService */ public synchronized void setJobServiceReference(final ExtensionReference serviceReference) { this.serviceReference = serviceReference; serviceWorker.addTask(new ServiceTask<Object>(new ServiceCallback<Object>() { public LibraryService execute(final LibraryService service) throws Exception { if (processService != null) { processService.stop(); processService.removeServiceListener(processServicelistener); } processJobs.clear(); processService = RenderServiceRegistry.getInstance().getSpoolRegistry().getExtension(serviceReference.getExtensionId()).createExtensionRuntime().getJobService(SERVICE_PROCESS, service, spoolWorker); processService.addServiceListener(processServicelistener); processService.start(); return null; } public void executed(final Object value) { } public void failed(final Throwable throwable) { } })); } /** * @return the serviceReference */ public synchronized ExtensionReference getJobServiceReference() { return serviceReference; } /** * */ public void start() { spoolWorker.start(); serviceWorker.start(); previewWorker.start(); postProcessService.start(); copyProcessService.start(); if (processService != null) { processService.start(); } } /** * */ public void stop() { spoolWorker.stop(); serviceWorker.stop(); previewWorker.stop(); copyProcessService.stop(); postProcessService.stop(); if (processService != null) { processService.stop(); } } /** * @return */ public LibraryService getLibraryService() { return service; } /** * @param task */ public <E> void execute(final ServiceCallback<E> callback) { serviceWorker.addTask(new ServiceTask<E>(callback)); } /** * @param listener */ public void addServiceListener(final LibraryServiceListener listener) { service.addServiceListener(listener); } /** * @param listener */ public void removeServiceListener(final LibraryServiceListener listener) { service.removeServiceListener(listener); } /** * @param listener */ public void addJobServiceListener(final JobServiceListener listener) { synchronized (listeners) { listeners.add(listener); } } /** * @param listener */ public void removeJobServiceListener(final JobServiceListener listener) { synchronized (listeners) { listeners.remove(listener); } } /** * @param listener */ public void addPreviewListener(final PreviewListener listener) { synchronized (previewListeners) { previewListeners.add(listener); } } /** * @param listener */ public void removePreviewListener(final PreviewListener listener) { synchronized (previewListeners) { previewListeners.remove(listener); } } /** * */ protected void firePreviewUpdated(final int clipId) { synchronized (previewListeners) { for (PreviewListener listener : previewListeners) { listener.updated(clipId); } } } /** * @return */ public ClipPreview getClipPreview(final int clipId) { return previews.get(clipId); } private class ServiceTask<T> implements Runnable { private final ServiceCallback<T> callback; /** * @param callback * @param hasValue */ public ServiceTask(final ServiceCallback<T> callback) { this.callback = callback; } public final void run() { try { callback.executed(callback.execute(service)); } catch (final Exception e) { e.printStackTrace(); callback.failed(e); } } } public interface ServiceCallback<T> { /** * @param service * @return * @throws Exception */ public T execute(LibraryService service) throws Exception; /** * @param throwable */ public void failed(Throwable throwable); /** * @param value */ public void executed(T value); } private class DefaultLibraryServiceListener implements LibraryServiceListener { /** * @see net.sf.jame.queue.LibraryServiceListener#clipCreated(net.sf.jame.queue.clip.RenderClipDataRow) */ public void clipCreated(final RenderClipDataRow clip) { ClipPreview preview = previews.get(clip.getClipId()); if (preview == null) { preview = new DefaultClipPreview(clip.getClipId()); previews.put(clip.getClipId(), preview); } preview.update(); } /** * @see net.sf.jame.queue.LibraryServiceListener#clipDeleted(net.sf.jame.queue.clip.RenderClipDataRow) */ public void clipDeleted(final RenderClipDataRow clip) { ClipPreview preview = previews.remove(clip.getClipId()); if (preview != null) { preview.dispose(); } } /** * @see net.sf.jame.queue.LibraryServiceListener#clipUpdated(net.sf.jame.queue.clip.RenderClipDataRow) */ public void clipUpdated(final RenderClipDataRow clip) { } /** * @see net.sf.jame.queue.LibraryServiceListener#clipUpdated(net.sf.jame.queue.clip.RenderClipDataRow) */ public void clipLoaded(final RenderClipDataRow clip) { ClipPreview preview = previews.get(clip.getClipId()); if (preview == null) { preview = new DefaultClipPreview(clip.getClipId()); previews.put(clip.getClipId(), preview); } preview.update(); } /** * @see net.sf.jame.queue.LibraryServiceListener#jobCreated(net.sf.jame.queue.job.RenderJobDataRow) */ public void jobCreated(final RenderJobDataRow job) { } /** * @see net.sf.jame.queue.LibraryServiceListener#jobDeleted(net.sf.jame.queue.job.RenderJobDataRow) */ public void jobDeleted(final RenderJobDataRow job) { if (job.isPostProcess()) { final String jobId = postProcessJobs.remove(job.getJobId()); if (jobId != null) { postProcessService.deleteJob(jobId); } } else if (job.isCopyProcess()) { final String jobId = copyProcessJobs.remove(job.getJobId()); if (jobId != null) { copyProcessService.deleteJob(jobId); } } else if (job.isProcess()) { final String jobId = processJobs.remove(job.getJobId()); if (jobId != null) { processService.deleteJob(jobId); } } } /** * @see net.sf.jame.queue.LibraryServiceListener#jobStarted(net.sf.jame.queue.job.RenderJobDataRow) */ public void jobStarted(final RenderJobDataRow job) { if (job.isPostProcess()) { if (!postProcessJobs.containsKey(job.getJobId())) { final String jobId = postProcessService.createJob(jobListener); postProcessService.setJobData(jobId, job, job.getFrameNumber()); postProcessJobs.put(job.getJobId(), jobId); if (jobId != null) { postProcessService.runJob(jobId); } } else { final String jobId = postProcessJobs.get(job.getJobId()); if (jobId != null) { postProcessService.runJob(jobId); } } } else if (job.isCopyProcess()) { if (!copyProcessJobs.containsKey(job.getJobId())) { final String jobId = copyProcessService.createJob(jobListener); copyProcessService.setJobData(jobId, job, job.getFrameNumber()); copyProcessJobs.put(job.getJobId(), jobId); if (jobId != null) { copyProcessService.runJob(jobId); } } else { final String jobId = copyProcessJobs.get(job.getJobId()); if (jobId != null) { copyProcessService.runJob(jobId); } } } else if (job.isProcess()) { if (!processJobs.containsKey(job.getJobId())) { final String jobId = processService.createJob(jobListener); processService.setJobData(jobId, job, job.getFrameNumber()); processJobs.put(job.getJobId(), jobId); if (jobId != null) { processService.runJob(jobId); } } else { final String jobId = processJobs.get(job.getJobId()); if (jobId != null) { processService.runJob(jobId); } } } } /** * @see net.sf.jame.queue.LibraryServiceListener#jobAborted(net.sf.jame.queue.job.RenderJobDataRow) */ public void jobAborted(final RenderJobDataRow job) { if (job.isPostProcess()) { final String jobId = postProcessJobs.get(job.getJobId()); if (jobId != null) { postProcessService.abortJob(jobId); } } else if (job.isCopyProcess()) { final String jobId = copyProcessJobs.get(job.getJobId()); if (jobId != null) { copyProcessService.abortJob(jobId); } } else if (job.isProcess()) { final String jobId = processJobs.get(job.getJobId()); if (jobId != null) { processService.abortJob(jobId); } } } /** * @see net.sf.jame.queue.LibraryServiceListener#jobStopped(net.sf.jame.queue.job.RenderJobDataRow) */ public void jobStopped(final RenderJobDataRow job) { if (job.isPostProcess()) { final String jobId = postProcessJobs.get(job.getJobId()); if (jobId != null) { postProcessService.stopJob(jobId); } } else if (job.isCopyProcess()) { final String jobId = copyProcessJobs.get(job.getJobId()); if (jobId != null) { copyProcessService.stopJob(jobId); } } else if (job.isProcess()) { final String jobId = processJobs.get(job.getJobId()); if (jobId != null) { processService.stopJob(jobId); } } } /** * @see net.sf.jame.queue.LibraryServiceListener#jobUpdated(net.sf.jame.queue.job.RenderJobDataRow) */ public void jobUpdated(final RenderJobDataRow job) { } /** * @see net.sf.jame.queue.LibraryServiceListener#jobResumed(net.sf.jame.queue.job.RenderJobDataRow) */ public void jobResumed(final RenderJobDataRow job) { if (job.isPostProcess()) { if (!postProcessJobs.containsKey(job.getJobId())) { final String jobId = postProcessService.createJob(jobListener); postProcessService.setJobData(jobId, job, job.getFrameNumber()); postProcessJobs.put(job.getJobId(), jobId); } } else if (job.isCopyProcess()) { if (!copyProcessJobs.containsKey(job.getJobId())) { final String jobId = copyProcessService.createJob(jobListener); copyProcessService.setJobData(jobId, job, job.getFrameNumber()); copyProcessJobs.put(job.getJobId(), jobId); } } else if (job.isProcess()) { if (!processJobs.containsKey(job.getJobId())) { final String jobId = processService.createJob(jobListener); processService.setJobData(jobId, job, job.getFrameNumber()); processJobs.put(job.getJobId(), jobId); } } } /** * @see net.sf.jame.queue.LibraryServiceListener#profileCreated(net.sf.jame.queue.profile.RenderProfileDataRow) */ public void profileCreated(final RenderProfileDataRow profile) { } /** * @see net.sf.jame.queue.LibraryServiceListener#profileDeleted(net.sf.jame.queue.profile.RenderProfileDataRow) */ public void profileDeleted(final RenderProfileDataRow profile) { } /** * @see net.sf.jame.queue.LibraryServiceListener#profileUpdated(net.sf.jame.queue.profile.RenderProfileDataRow) */ public void profileUpdated(final RenderProfileDataRow profile) { } /** * @see net.sf.jame.queue.LibraryServiceListener#profileLoaded(net.sf.jame.queue.profile.RenderProfileDataRow) */ public void profileLoaded(final RenderProfileDataRow profile) { } } private class DefaultJobListener implements JobListener { /** * @see net.sf.jame.queue.spool.JobListener#updated(String, JobData) */ public void updated(final String jobId, final JobData jobData) { execute(new ServiceCallback<Object>() { public Object execute(final LibraryService service) throws Exception { try { service.processUpdated(jobData); } catch (Exception e) { } return null; } public void executed(final Object value) { } public void failed(final Throwable throwable) { } }); } /** * @see net.sf.jame.queue.spool.JobListener#started(String, JobData) */ public void started(final String jobId, final JobData jobData) { execute(new ServiceCallback<Object>() { public Object execute(final LibraryService service) throws Exception { try { service.processStarted(jobData); } catch (Exception e) { } return null; } public void executed(final Object value) { } public void failed(final Throwable throwable) { } }); } /** * @see net.sf.jame.queue.spool.JobListener#stopped(String, JobData) */ public void stopped(final String jobId, final JobData jobData) { execute(new ServiceCallback<Object>() { public Object execute(final LibraryService service) throws Exception { try { service.processStopped(jobData); } catch (Exception e) { } return null; } public void executed(final Object value) { } public void failed(final Throwable throwable) { } }); } /** * @see net.sf.jame.queue.spool.JobListener#terminated(String, JobData) */ public void terminated(final String jobId, final JobData jobData) { execute(new ServiceCallback<Object>() { public Object execute(final LibraryService service) throws Exception { try { service.processTerminated(jobData); } catch (Exception e) { } return null; } public void executed(final Object value) { } public void failed(final Throwable throwable) { } }); } /** * @see net.sf.jame.queue.spool.JobListener#terminated(String, JobData) */ public void disposed(final String jobId, final JobData jobData) { execute(new ServiceCallback<Object>() { public Object execute(final LibraryService service) throws Exception { try { service.processDeleted(jobData); } catch (Exception e) { } return null; } public void executed(final Object value) { } public void failed(final Throwable throwable) { } }); } } private class DefaultJobServiceListener implements JobServiceListener { private final int serviceId; /** * @param serviceId */ public DefaultJobServiceListener(final int serviceId) { this.serviceId = serviceId; } public void stateChanged(final int serviceId, final int status, final String message) { synchronized (listeners) { for (JobServiceListener listener : listeners) { listener.stateChanged(DefaultJobServiceListener.this.serviceId, status, message); } } } } private class DefaultClipPreview implements ClipPreview { private final int clipId; private Surface surface; /** * @param clipId */ public DefaultClipPreview(final int clipId) { this.clipId = clipId; surface = new Surface(20, 20); } private void render(final int clipId) { previewWorker.addTask(new Runnable() { public void run() { try { RenderClipDataRow dataRow = service.getClip(clipId); final TwisterClip clip = service.loadClip(dataRow); if (clip.getSequenceCount() > 0) { TwisterRuntime runtime = new TwisterRuntime(clip.getSequence(0).getFinalConfig()); DefaultTwisterRenderer renderer = new DefaultTwisterRenderer(runtime); final IntegerVector2D size = new IntegerVector2D(surface.getWidth(), surface.getHeight()); renderer.setTile(new Tile(size, size, new IntegerVector2D(0, 0), new IntegerVector2D(0, 0))); renderer.prepareImage(false); renderer.startRenderer(); renderer.joinRenderer(); renderer.drawSurface(surface.getGraphics2D()); renderer.dispose(); firePreviewUpdated(clipId); } } catch (Exception e) { } } }); } /** * @see net.sf.jame.queue.clip.ClipPreview#getImage() */ public Image getImage() { return surface.getImage(); } /** * @see net.sf.jame.queue.clip.ClipPreview#dispose() */ public void dispose() { if (surface != null) { surface.dispose(); surface = null; } } /** * @see net.sf.jame.queue.clip.ClipPreview#update() */ public void update() { render(clipId); } } }