/*
* Tanaguru - Automated webpage assessment
* Copyright (C) 2008-2015 Tanaguru.org
*
* This file is part of Tanaguru.
*
* Tanaguru is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Contact us by mail: tanaguru AT tanaguru DOT org
*/
package org.tanaguru.service;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.log4j.Logger;
import org.tanaguru.entity.audit.Audit;
import org.tanaguru.service.command.AuditCommand;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
* @author enzolalay
*/
public class AuditServiceThreadQueueImpl implements AuditServiceThreadQueue, AuditServiceThreadListener {
private static final Logger LOGGER = Logger.getLogger(AuditServiceThreadQueueImpl.class);
private final Queue<AuditCommand> pageAuditWaitQueue = new ConcurrentLinkedQueue<>();
private final Queue<AuditCommand> scenarioAuditWaitQueue = new ConcurrentLinkedQueue<>();
private final Queue<AuditCommand> uploadAuditWaitQueue = new ConcurrentLinkedQueue<>();
private final Queue<AuditCommand> siteAuditWaitQueue = new ConcurrentLinkedQueue<>();
private final List<AuditServiceThread> pageAuditExecutionList = new ArrayList<>();
private final List<AuditServiceThread> scenarioAuditExecutionList = new ArrayList<>();
private final List<AuditServiceThread> uploadAuditExecutionList = new ArrayList<>();
private final List<AuditServiceThread> siteAuditExecutionList = new ArrayList<>();
private static final int MAX_AUDIT_EXECUTION_LIST_VALUE = 3;
private int pageAuditExecutionListMax = MAX_AUDIT_EXECUTION_LIST_VALUE;
public int getPageAuditExecutionListMax() {
return pageAuditExecutionListMax;
}
@Override
public void setPageAuditExecutionListMax(int max) {
this.pageAuditExecutionListMax = max;
}
private int scenarioAuditExecutionListMax = MAX_AUDIT_EXECUTION_LIST_VALUE;
public int getScenarioAuditExecutionListMax() {
return scenarioAuditExecutionListMax;
}
@Override
public void setScenarioAuditExecutionListMax(int max) {
this.scenarioAuditExecutionListMax = max;
}
private int uploadAuditExecutionListMax = MAX_AUDIT_EXECUTION_LIST_VALUE;
public int getUploadAuditExecutionListMax() {
return uploadAuditExecutionListMax;
}
@Override
public void setUploadAuditExecutionListMax(int max) {
this.uploadAuditExecutionListMax = max;
}
private int siteAuditExecutionListMax = MAX_AUDIT_EXECUTION_LIST_VALUE;
public int getSiteAuditExecutionListMax() {
return siteAuditExecutionListMax;
}
@Override
public void setSiteAuditExecutionListMax(int max) {
this.siteAuditExecutionListMax = max;
}
private Set<AuditServiceListener> listeners;
public Set<AuditServiceListener> getListeners() {
return listeners;
}
private AuditServiceThreadFactory auditServiceThreadFactory;
@Autowired
public void setAuditServiceThreadFactory(AuditServiceThreadFactory auditServiceThreadFactory) {
this.auditServiceThreadFactory = auditServiceThreadFactory;
}
public AuditServiceThreadQueueImpl() {
super();
}
@Override
public void add(AuditServiceListener listener) {
if (listeners == null) {
listeners = new HashSet<>();
}
listeners.add(listener);
}
@Override
public void remove(AuditServiceListener listener) {
if (listeners == null) {
return;
}
listeners.remove(listener);
}
@Override
public synchronized void addPageAudit(AuditCommand auditCommand) {
pageAuditWaitQueue.offer(auditCommand);
processPageAuditWaitQueue();
}
private synchronized void processPageAuditWaitQueue() {
processAuditWaitQueue(
pageAuditWaitQueue,
pageAuditExecutionList,
pageAuditExecutionListMax);
}
@Override
public synchronized void addScenarioAudit(AuditCommand auditCommand) {
scenarioAuditWaitQueue.offer(auditCommand);
processScenarioAuditWaitQueue();
}
private synchronized void processScenarioAuditWaitQueue() {
processAuditWaitQueue(
scenarioAuditWaitQueue,
scenarioAuditExecutionList,
scenarioAuditExecutionListMax);
}
@Override
public synchronized void addPageUploadAudit(AuditCommand auditCommand) {
uploadAuditWaitQueue.offer(auditCommand);
processPageUploadAuditWaitQueue();
}
private synchronized void processPageUploadAuditWaitQueue() {
processAuditWaitQueue(
uploadAuditWaitQueue,
uploadAuditExecutionList,
uploadAuditExecutionListMax);
}
@Override
public synchronized void addSiteAudit(AuditCommand auditCommand) {
siteAuditWaitQueue.offer(auditCommand);
processSiteAuditWaitQueue();
}
private synchronized void processSiteAuditWaitQueue() {
processAuditWaitQueue(
siteAuditWaitQueue,
siteAuditExecutionList,
siteAuditExecutionListMax);
}
/**
*
* @param auditWaitQueue
* @param auditExecutionList
* @param auditExecutionListMax
* @return
*/
private synchronized void processAuditWaitQueue(
Queue<AuditCommand> auditWaitQueue,
List<AuditServiceThread> auditExecutionList,
int auditExecutionListMax) {
if (auditWaitQueue.peek() == null) {
return;
}
if (auditExecutionList.size() < auditExecutionListMax) {
AuditCommand auditCommand = auditWaitQueue.poll();
LOGGER.debug("auditCommand polled");
AuditServiceThread auditServiceThread = auditServiceThreadFactory.create(auditCommand);
LOGGER.debug("AuditServiceThread created from auditCommand");
auditServiceThread.add(this);
auditExecutionList.add(auditServiceThread);
new Thread(auditServiceThread).start();
LOGGER.debug("AuditServiceThread started");
} else {
LOGGER.debug("Execution requested but max simultaneous execution reached");
}
}
@Override
public void auditCompleted(AuditServiceThread thread) {
fireAuditCompleted(thread.getAudit());
terminateProperlyAudit(thread);
}
@Override
public void auditCrashed(AuditServiceThread thread, Exception exception) {
fireAuditCrashed(thread.getAudit(), exception);
terminateProperlyAudit(thread);
}
/**
* Interrogate each execution List to properly remove the current thread
* and eventually launch audit waiting in the queue
* @param thread
*/
private void terminateProperlyAudit(AuditServiceThread thread){
thread.remove(this);
if (pageAuditExecutionList.remove(thread)) {
processPageAuditWaitQueue();
} else if (scenarioAuditExecutionList.remove(thread)) {
processScenarioAuditWaitQueue();
} else if (uploadAuditExecutionList.remove(thread)) {
processPageUploadAuditWaitQueue();
} else if (siteAuditExecutionList.remove(thread)) {
processSiteAuditWaitQueue();
}
}
@Override
public void processWaitQueue() {
}
private void fireAuditCompleted(Audit audit) {
if (listeners == null) {
return;
}
for (AuditServiceListener listener : listeners) {
listener.auditCompleted(audit);
}
}
private void fireAuditCrashed(Audit audit, Exception exception) {
if (listeners == null) {
return;
}
for (AuditServiceListener listener : listeners) {
listener.auditCrashed(audit, exception);
}
}
}