/*******************************************************************************
* Copyright (c) 2011 Subgraph.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Subgraph - initial API and implementation
******************************************************************************/
package com.subgraph.vega.internal.model.alerts;
import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
import org.apache.http.client.methods.HttpUriRequest;
import com.db4o.ObjectContainer;
import com.db4o.activation.ActivationPurpose;
import com.db4o.activation.Activator;
import com.db4o.query.Predicate;
import com.db4o.ta.Activatable;
import com.subgraph.vega.api.events.EventListenerManager;
import com.subgraph.vega.api.events.IEventHandler;
import com.subgraph.vega.api.model.alerts.IScanAlert;
import com.subgraph.vega.api.model.alerts.IScanInstance;
import com.subgraph.vega.api.model.alerts.NewScanAlertEvent;
import com.subgraph.vega.api.model.alerts.ScanExceptionEvent;
import com.subgraph.vega.api.model.alerts.ScanStatusChangeEvent;
import com.subgraph.vega.internal.model.ModelProperties;
public class ScanInstance implements IScanInstance, Activatable {
private final static Logger logger = Logger.getLogger("alerts");
private final long scanId;
private final ModelProperties properties;
private final Date startTime;
private int scanStatus;
private transient EventListenerManager eventManager;
private transient ObjectContainer database;
private transient ScanAlertFactory alertFactory;
private transient Lock lock;
private transient int activeScanCompletedCount;
private transient int activeScanTotalCount;
private transient Activator activator;
ScanInstance(long scanId) {
this.scanId = scanId;
this.scanStatus = SCAN_IDLE;
this.properties = new ModelProperties();
this.startTime = new Date();
}
void setTransientState(ObjectContainer database, ScanAlertFactory alertFactory) {
this.database = database;
this.alertFactory = alertFactory;
this.lock = new ReentrantLock();
this.eventManager = new EventListenerManager();
}
@Override
public long getScanId() {
activate(ActivationPurpose.READ);
return scanId;
}
@Override
public Date getStartTime() {
activate(ActivationPurpose.READ);
return startTime;
}
@Override
public IScanAlert createAlert(String type) {
return createAlert(type, null, -1);
}
@Override
public IScanAlert createAlert(String type, String key) {
return createAlert(type, key, -1);
}
@Override
public IScanAlert createAlert(String type, String key, long requestId) {
activate(ActivationPurpose.READ);
return alertFactory.createAlert(key, type, scanId, requestId);
}
@Override
public void addAlert(IScanAlert alert) {
activate(ActivationPurpose.READ);
if(rejectDuplicateAlert(alert)) {
return;
}
database.store(alert);
eventManager.fireEvent(new NewScanAlertEvent(alert));
}
@Override
public boolean hasAlertKey(String key) {
return getAlertByKey(key) != null;
}
@Override
public IScanAlert getAlertByKey(String key) {
if(key == null) {
return null;
}
synchronized (this) {
final List<ScanAlert> results = getAlertListForKey(key);
if(results.size() == 0) {
return null;
}
if(results.size() > 1) {
logger.warning("Multiple alert model entries for key: "+ key);
}
return results.get(0);
}
}
private List<ScanAlert> getAlertListForKey(final String key) {
activate(ActivationPurpose.READ);
return database.query(new Predicate<ScanAlert>() {
private static final long serialVersionUID = 1L;
@Override
public boolean match(ScanAlert alert) {
return key.equals(alert.getKey()) && alert.getScanId() == scanId;
}
});
}
@Override
public List<IScanAlert> getAllAlerts() {
activate(ActivationPurpose.READ);
return database.query(new Predicate<IScanAlert>() {
private static final long serialVersionUID = 1L;
@Override
public boolean match(IScanAlert alert) {
return alert.getScanId() == scanId;
}
});
}
@Override
public void addScanEventListenerAndPopulate(IEventHandler listener) {
lock();
try {
for(IScanAlert alert: getAllAlerts()) {
listener.handleEvent(new NewScanAlertEvent(alert));
}
listener.handleEvent(new ScanStatusChangeEvent(scanStatus, activeScanCompletedCount, activeScanTotalCount));
eventManager.addListener(listener);
} finally {
unlock();
}
}
@Override
public void removeScanEventListener(IEventHandler listener) {
eventManager.removeListener(listener);
}
@Override
public int getScanStatus() {
activate(ActivationPurpose.READ);
return scanStatus;
}
@Override
public int getScanCompletedCount() {
return activeScanCompletedCount;
}
@Override
public int getScanTotalCount() {
return activeScanTotalCount;
}
@Override
public void updateScanProgress(int completedCount, int totalCount) {
activeScanCompletedCount = completedCount;
activeScanTotalCount = totalCount;
eventManager.fireEvent(new ScanStatusChangeEvent(scanStatus, activeScanCompletedCount, activeScanTotalCount));
}
@Override
public void updateScanStatus(int status) {
activate(ActivationPurpose.WRITE);
this.scanStatus = status;
eventManager.fireEvent(new ScanStatusChangeEvent(scanStatus, activeScanCompletedCount, activeScanTotalCount));
}
@Override
public void notifyScanException(HttpUriRequest request, Throwable exception) {
eventManager.fireEvent(new ScanExceptionEvent(request, exception));
}
@Override
public void lock() {
lock.lock();
}
@Override
public void unlock() {
lock.unlock();
}
@Override
public void setProperty(String name, Object value) {
activate(ActivationPurpose.READ);
properties.setProperty(name, value);
}
@Override
public void setStringProperty(String name, String value) {
activate(ActivationPurpose.READ);
properties.setStringProperty(name, value);
}
@Override
public void setIntegerProperty(String name, int value) {
activate(ActivationPurpose.READ);
properties.setIntegerProperty(name, value);
}
@Override
public Object getProperty(String name) {
activate(ActivationPurpose.READ);
return properties.getProperty(name);
}
@Override
public String getStringProperty(String name) {
activate(ActivationPurpose.READ);
return properties.getStringProperty(name);
}
@Override
public Integer getIntegerProperty(String name) {
activate(ActivationPurpose.READ);
return properties.getIntegerProperty(name);
}
@Override
public List<String> propertyKeys() {
activate(ActivationPurpose.READ);
return properties.propertyKeys();
}
private boolean rejectDuplicateAlert(IScanAlert alert) {
if(alert.getResource() == null) {
return false;
}
for(ScanAlert sa: getAlertListForResource(alert.getResource())) {
if(sa.equals(alert)) {
return true;
}
}
if(alert.getKey() == null) {
return false;
} else {
return hasAlertKey(alert.getKey());
}
}
private List<ScanAlert> getAlertListForResource(final String resource) {
activate(ActivationPurpose.READ);
return database.query(new Predicate<ScanAlert>() {
private static final long serialVersionUID = 1L;
@Override
public boolean match(ScanAlert alert) {
return resource.equals(alert.getResource()) && alert.getScanId() == scanId;
}
});
}
@Override
public void activate(ActivationPurpose activationPurpose) {
if(activator != null) {
activator.activate(activationPurpose);
}
}
@Override
public void bind(Activator activator) {
if(this.activator == activator) {
return;
}
if(activator != null && this.activator != null) {
throw new IllegalStateException("Object can only be bound to one activator");
}
this.activator = activator;
}
}