/*
* Copyright 2009 VoidSearch.com
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.voidsearch.voidbase.storage.jobqueue;
import com.voidsearch.voidbase.storage.AsynchronousStorage;
import com.voidsearch.voidbase.storage.SupervisedStorage;
import com.voidsearch.voidbase.storage.StorageOperation;
import com.voidsearch.voidbase.supervision.SupervisionException;
import com.voidsearch.voidbase.supervision.StorageStats;
import com.voidsearch.voidbase.config.VoidBaseConfiguration;
import com.voidsearch.voidbase.config.Config;
import java.util.concurrent.*;
import java.util.HashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JobQueue implements AsynchronousStorage, SupervisedStorage, Runnable {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private int POLL_INTERVAL = 1000;
private long QUEUE_WAIT_INTERVAL = 1000;
private Long queryCounter = new Long(0);
// job queue
private PriorityBlockingQueue<JobRequest> requestQueue = new PriorityBlockingQueue<JobRequest>();
private ConcurrentHashMap<JobRequest,CountDownLatch> pendingJobs = new ConcurrentHashMap<JobRequest, CountDownLatch>();
private HashSet<JobRequest> canceledJobs = new HashSet<JobRequest>();
private ConcurrentHashMap<JobRequest,JobResult> doneJobs = new ConcurrentHashMap<JobRequest,JobResult>();
// oplocks
private HashSet<StorageOperation> opLocks = new HashSet<StorageOperation>();
public JobQueue() {
if (VoidBaseConfiguration.contains(Config.STORAGE, "JobQueue", "cleanupInterval")) {
try {
POLL_INTERVAL = Integer.parseInt(VoidBaseConfiguration.get(Config.STORAGE, "JobQueue", "cleanupInterval"));
} catch (Exception e) {
}
}
// spawn threads
(new Thread(this)).start();
(new RequestExecutor(this)).start();
}
public void registerDequeuer() {
}
// storage operations
public void put(JobRequest req) throws SupervisionException {
queryCounter++;
if (opLocks.contains(StorageOperation.PUT))
throw new SupervisionException();
CountDownLatch pending = new CountDownLatch(1);
pendingJobs.put(req,pending);
requestQueue.put(req);
}
public JobResult get(JobRequest req) throws SupervisionException {
queryCounter++;
if (opLocks.contains(StorageOperation.GET))
throw new SupervisionException();
JobResult result = null;
if (pendingJobs.containsKey(req)) {
try {
(pendingJobs.get(req)).await();
if (doneJobs.containsKey(req)) {
result = doneJobs.get(req);
doneJobs.remove(req);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
pendingJobs.remove(req);
}
return result;
}
public JobRequest poll() throws InterruptedException {
return requestQueue.poll(QUEUE_WAIT_INTERVAL,TimeUnit.MILLISECONDS);
}
public void addDone(JobRequest request, JobResult result) {
doneJobs.put(request,result);
}
// supervision support
public void blockOperation(StorageOperation op) throws SupervisionException {
opLocks.add(op);
}
public void enableOperation(StorageOperation op) throws SupervisionException {
if (opLocks.contains(op)) {
opLocks.remove(op);
}
}
public void updateStats(StorageStats stats) {
stats.setCounter(StorageStats.Counter.TOTAL_ENTRIES,requestQueue.size());
stats.setCounter(StorageStats.Counter.QUERY_COUNT,getTotalQueries());
}
public long getTotalQueries() {
return queryCounter;
}
public long getMemorySize() {
return requestQueue.size();
}
//
// request expiration thread
//
public void run() {
while(true) {
for (JobRequest req : requestQueue) {
if (!canceledJobs.contains(req) && req.isExpired()) {
canceledJobs.add(req);
logger.info("request : " + req + " expired");
if (pendingJobs.containsKey(req)) {
(pendingJobs.get(req)).countDown();
}
}
}
try {
Thread.sleep(POLL_INTERVAL);
} catch (Exception e) { }
}
}
public class RequestExecutor extends Thread {
JobQueue queue;
public RequestExecutor(JobQueue queue) {
this.queue = queue;
}
public void run() {
while (true) {
try {
JobRequest request = queue.poll();
if (request != null) {
if (!canceledJobs.contains(request)) {
JobResult result = request.execute();
if (pendingJobs.containsKey(request)) {
(pendingJobs.get(request)).countDown();
queue.addDone(request, result);
}
}
} else {
// poll timeouted
// handle timeout ? - or just retry ?
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}