/*
* 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.network.spool;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Logger;
import net.sf.jame.core.util.DefaultThreadFactory;
import net.sf.jame.queue.network.EventMessage;
import net.sf.jame.queue.network.RequestMessage;
import net.sf.jame.queue.network.ResponseMessage;
import net.sf.jame.queue.network.ServiceException;
import net.sf.jame.queue.network.ServiceMessage;
import net.sf.jame.queue.network.ServiceProcessor;
import net.sf.jame.queue.network.ServiceSession;
import net.sf.jame.queue.network.SessionHandler;
import net.sf.jame.queue.spool.JobData;
import net.sf.jame.queue.spool.JobEvent;
import net.sf.jame.queue.spool.JobListener;
import net.sf.jame.queue.spool.JobService;
import net.sf.jame.queue.spool.JobStatus;
import net.sf.jame.queue.spool.SpoolJobInterface;
import net.sf.jame.queue.spool.job.LocalSpoolJob;
/**
* @author Andrea Medeghini
*/
public class LocalServiceProcessor implements ServiceProcessor {
private static final Logger logger = Logger.getLogger(LocalServiceProcessor.class.getName());
private static final ThreadFactory factory = new DefaultThreadFactory("Thread", true, Thread.MIN_PRIORITY);
private final List<ExecutorTask> tasks = new LinkedList<ExecutorTask>();
private final Object monitor = new Object();
private final JobService<? extends SpoolJobInterface> jobService;
private Thread thread;
private boolean running;
private boolean dirty;
/**
* @param service
* @param jobService
* @param maxJobCount
*/
public LocalServiceProcessor(final JobService<LocalSpoolJob> jobService, final int maxJobCount) {
this.jobService = jobService;
}
/**
* @see net.sf.jame.queue.network.ServiceProcessor#start()
*/
public void start() {
jobService.start();
if (thread == null) {
thread = factory.newThread(new ExecutorHandler());
thread.setName(jobService.getName() + " Executor Thread");
running = true;
thread.start();
}
}
/**
* @see net.sf.jame.queue.network.ServiceProcessor#stop()
*/
public void stop() {
jobService.stop();
if (thread != null) {
running = false;
thread.interrupt();
try {
thread.join();
}
catch (final InterruptedException e) {
}
thread = null;
}
}
/**
* @see net.sf.jame.queue.network.ServiceProcessor#createSessionHandler()
*/
public SessionHandler createSessionHandler() {
return new SpoolSessionHandler();
}
private class ExecutorHandler implements Runnable {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
final long pollingTime = 60 * 1000L;
final List<ExecutorTask> tasksToRun = new LinkedList<ExecutorTask>();
try {
while (running) {
try {
synchronized (tasks) {
tasksToRun.addAll(tasks);
tasks.clear();
}
for (final ExecutorTask task : tasksToRun) {
task.run();
Thread.yield();
}
tasksToRun.clear();
}
catch (final Exception e) {
e.printStackTrace();
}
synchronized (monitor) {
if (!dirty) {
monitor.wait(pollingTime);
}
dirty = false;
}
}
}
catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
private class ExecutorTask implements Runnable {
private final ServiceSession session;
private final ServiceMessage message;
/**
* @param session
* @param message
*/
public ExecutorTask(final ServiceSession session, final ServiceMessage message) {
this.session = session;
this.message = message;
}
/**
* @see java.lang.Runnable#run()
*/
public void run() {
try {
logger.fine("Ready to send message " + message + " on session " + session.getSessionId());
if (!session.isExpired()) {
session.sendMessage(message);
}
else {
logger.warning("Failed to send the message " + message + " on session " + session.getSessionId());
logger.warning("Invalidate session " + session.getSessionId());
session.invalidate();
}
}
catch (final Exception e) {
logger.warning("Failed to send the message " + message + " on session " + session.getSessionId());
logger.warning("Invalidate session " + session.getSessionId());
session.invalidate();
e.printStackTrace();
}
}
}
private class SpoolSessionHandler implements SessionHandler {
private ServiceSession session;
/**
* @see net.sf.jame.queue.network.SessionHandler#dispose()
*/
public void dispose() {
if (session != null) {
session.dispose();
session = null;
}
}
/**
* @see net.sf.jame.queue.network.SessionHandler#setSession(net.sf.jame.queue.network.ServiceSession)
*/
public void setSession(final ServiceSession session) {
if (session == null) {
throw new IllegalArgumentException("session == null");
}
this.session = session;
}
/**
* @see net.sf.jame.queue.network.SessionHandler#getSession()
*/
public ServiceSession getSession() {
return session;
}
/**
* @see net.sf.jame.queue.network.SessionHandler#isExpired()
*/
public boolean isExpired() {
return (session == null) || session.isExpired();
}
/**
* @see net.sf.jame.queue.network.SessionHandler#onMessage(net.sf.jame.queue.network.ServiceMessage)
*/
public void onMessage(final ServiceMessage message) throws ServiceException {
if (isExpired()) {
return;
}
try {
switch (message.getMessageType()) {
case ServiceMessage.MESSAGE_TYPE_REQUEST: {
final RequestMessage request = (RequestMessage) message;
switch (request.getRequestType()) {
case RequestMessage.TYPE_HELLO: {
processHelloRequest(request);
break;
}
case RequestMessage.TYPE_PUT: {
processPutRequest(request);
break;
}
case RequestMessage.TYPE_GET: {
processGetRequest(request);
break;
}
case RequestMessage.TYPE_ABORT: {
processAbortRequest(request);
break;
}
case RequestMessage.TYPE_DELETE: {
processDeleteRequest(request);
break;
}
default: {
break;
}
}
break;
}
case ServiceMessage.MESSAGE_TYPE_RESPONSE: {
break;
}
case ServiceMessage.MESSAGE_TYPE_EVENT: {
break;
}
default: {
break;
}
}
}
catch (final Exception e) {
e.printStackTrace();
}
synchronized (monitor) {
dirty = true;
monitor.notify();
}
}
private void processDeleteRequest(final RequestMessage request) throws Exception {
final String jobId = (String) request.getUserData();
jobService.deleteJob(jobId);
synchronized (tasks) {
final ResponseMessage response = createDeleteResponse(request, jobId);
tasks.add(new ExecutorTask(session, response));
}
}
private void processAbortRequest(final RequestMessage request) throws Exception {
final String jobId = (String) request.getUserData();
jobService.abortJob(jobId);
jobService.stopJob(jobId);
synchronized (tasks) {
final ResponseMessage response = createAbortResponse(request, jobId);
tasks.add(new ExecutorTask(session, response));
}
}
private void processGetRequest(final RequestMessage request) throws Exception {
final String jobId = (String) ((Object[]) request.getUserData())[0];
final int frameNumber = (Integer) ((Object[]) request.getUserData())[1];
synchronized (tasks) {
final ResponseMessage response = createGetResponse(request, jobId, frameNumber);
tasks.add(new ExecutorTask(session, response));
}
}
private void processPutRequest(final RequestMessage request) throws Exception {
final String jobId = (String) ((Object[]) request.getUserData())[0];
final int frameNumber = (Integer) ((Object[]) request.getUserData())[1];
final byte[] data = (byte[]) ((Object[]) request.getUserData())[2];
final ByteArrayInputStream bais = new ByteArrayInputStream(data);
final ObjectInputStream ois = new ObjectInputStream(bais);
final JobData jobData = (JobData) ois.readObject();
ois.close();
bais.close();
jobService.setJobData(jobId, jobData, frameNumber);
jobService.runJob(jobId);
synchronized (tasks) {
final ResponseMessage response = createPutResponse(request, jobId);
tasks.add(new ExecutorTask(session, response));
}
}
private void processHelloRequest(final RequestMessage request) throws Exception {
final String jobId = jobService.createJob(new SpoolJobListener(session));
int jobCount = jobService.getJobCount();
if (jobId != null) {
synchronized (tasks) {
final ResponseMessage response = createHelloResponse(request, jobCount, jobId);
tasks.add(new ExecutorTask(session, response));
}
}
else {
synchronized (tasks) {
final ResponseMessage response = createHelloResponse(request, jobCount, null);
tasks.add(new ExecutorTask(session, response));
}
}
}
private ResponseMessage createHelloResponse(final RequestMessage request, final Integer jobCount, final String jobId) throws Exception {
final ResponseMessage message = new ResponseMessage();
message.setRequestId(request.getRequestId());
message.setResponseType(RequestMessage.TYPE_HELLO);
if (jobId != null) {
message.setUserData(new Object[] { jobCount, jobId });
message.setReturnCode(0);
}
else {
message.setUserData(new Object[] { jobCount, null });
message.setReturnCode(1);
}
return message;
}
private ResponseMessage createPutResponse(final RequestMessage request, final String jobId) throws Exception {
final ResponseMessage message = new ResponseMessage();
message.setRequestId(request.getRequestId());
message.setResponseType(RequestMessage.TYPE_PUT);
if (jobId != null) {
message.setUserData(jobId);
message.setReturnCode(0);
}
else {
message.setReturnCode(1);
}
return message;
}
private ResponseMessage createGetResponse(final RequestMessage request, final String jobId, final int frameNumber) throws Exception {
final ResponseMessage message = new ResponseMessage();
message.setRequestId(request.getRequestId());
message.setResponseType(RequestMessage.TYPE_GET);
if (jobId != null) {
message.setUserData(new Object[] { jobId, frameNumber, null });
message.setReturnCode(0);
}
else {
message.setReturnCode(1);
}
return message;
}
private ResponseMessage createAbortResponse(final RequestMessage request, final String jobId) throws Exception {
final ResponseMessage message = new ResponseMessage();
message.setRequestId(request.getRequestId());
message.setResponseType(RequestMessage.TYPE_ABORT);
if (jobId != null) {
message.setUserData(jobId);
message.setReturnCode(0);
}
else {
message.setReturnCode(1);
}
return message;
}
private ResponseMessage createDeleteResponse(final RequestMessage request, final String jobId) throws Exception {
final ResponseMessage message = new ResponseMessage();
message.setRequestId(request.getRequestId());
message.setResponseType(RequestMessage.TYPE_DELETE);
if (jobId != null) {
message.setUserData(jobId);
message.setReturnCode(0);
}
else {
message.setReturnCode(1);
}
return message;
}
}
private class SpoolJobListener implements JobListener {
private final ServiceSession session;
/**
* @param session
*/
public SpoolJobListener(final ServiceSession session) {
this.session = session;
}
/**
* @see net.sf.jame.queue.spool.JobListener#stateChanged(String, JobData)
*/
public void updated(final String jobId, final JobData job) {
if (session.isExpired()) {
return;
}
processUpdated(jobId, job);
}
/**
* @see net.sf.jame.queue.spool.JobListener#started(String, JobData)
*/
public void started(final String jobId, final JobData job) {
if (session.isExpired()) {
return;
}
processStarted(jobId, job);
}
/**
* @see net.sf.jame.queue.spool.JobListener#stopped(String, JobData)
*/
public void stopped(final String jobId, final JobData job) {
if (session.isExpired()) {
return;
}
processStopped(jobId, job);
}
/**
* @see net.sf.jame.queue.spool.JobListener#terminated(String, JobData)
*/
public void terminated(final String jobId, final JobData job) {
if (session.isExpired()) {
return;
}
processTerminated(jobId, job);
}
/**
* @see net.sf.jame.queue.spool.JobListener#disposed(String, JobData)
*/
public void disposed(final String jobId, final JobData job) {
if (session.isExpired()) {
return;
}
processDisposed(jobId, job);
}
private void processUpdated(final String jobId, final JobData job) {
int jobCount = jobService.getJobCount();
synchronized (tasks) {
try {
final EventMessage message = createFrameMessage(jobId, job, jobCount);
tasks.add(new ExecutorTask(session, message));
}
catch (final Exception e) {
e.printStackTrace();
}
}
synchronized (monitor) {
dirty = true;
monitor.notify();
}
}
private void processStarted(final String jobId, final JobData job) {
int jobCount = jobService.getJobCount();
synchronized (tasks) {
try {
final EventMessage message = createStartMessage(jobId, job, jobCount);
tasks.add(new ExecutorTask(session, message));
}
catch (final Exception e) {
e.printStackTrace();
}
}
synchronized (monitor) {
dirty = true;
monitor.notify();
}
}
private void processStopped(final String jobId, final JobData job) {
int jobCount = jobService.getJobCount();
synchronized (tasks) {
try {
final EventMessage message = createStopMessage(jobId, job, jobCount);
tasks.add(new ExecutorTask(session, message));
}
catch (final Exception e) {
e.printStackTrace();
}
}
synchronized (monitor) {
dirty = true;
monitor.notify();
}
}
private void processTerminated(final String jobId, final JobData job) {
int jobCount = jobService.getJobCount();
synchronized (tasks) {
try {
final EventMessage message = createDoneMessage(jobId, job, jobCount);
tasks.add(new ExecutorTask(session, message));
}
catch (final Exception e) {
e.printStackTrace();
}
}
synchronized (monitor) {
dirty = true;
monitor.notify();
}
}
private void processDisposed(final String jobId, final JobData job) {
synchronized (monitor) {
dirty = true;
monitor.notify();
}
}
private EventMessage createFrameMessage(final String jobId, final JobData job, final int jobCount) throws Exception {
final EventMessage message = new EventMessage();
message.setUserData(new JobEvent(JobEvent.EVENT_TYPE_FRAME, new JobStatus(jobId, job.getFrameNumber(), jobCount)));
return message;
}
private EventMessage createStartMessage(final String jobId, final JobData job, final int jobCount) throws Exception {
final EventMessage message = new EventMessage();
message.setUserData(new JobEvent(JobEvent.EVENT_TYPE_BEGIN, new JobStatus(jobId, job.getFrameNumber(), jobCount)));
return message;
}
private EventMessage createStopMessage(final String jobId, final JobData job, final int jobCount) throws Exception {
final EventMessage message = new EventMessage();
message.setUserData(new JobEvent(JobEvent.EVENT_TYPE_END, new JobStatus(jobId, job.getFrameNumber(), jobCount)));
return message;
}
private EventMessage createDoneMessage(final String jobId, final JobData job, final int jobCount) throws Exception {
final EventMessage message = new EventMessage();
message.setUserData(new JobEvent(JobEvent.EVENT_TYPE_DONE, new JobStatus(jobId, job.getFrameNumber(), jobCount)));
return message;
}
}
}