package sushi.correlation;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MapKeyColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Transient;
import sushi.event.SushiEvent;
import sushi.event.SushiEventType;
import sushi.event.attribute.SushiAttribute;
import sushi.persistence.Persistable;
import sushi.process.SushiProcess;
/**
*
* Container object for advanced correlation over time. Used for event correlation.
* Related to a process.
*
* @author Micha
* @author Tsun
*/
@Entity
@Table(name = "TimeCondition")
public class TimeCondition extends Persistable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int ID;
@ManyToOne
private SushiEventType selectedEventType;
@Column(name = "ConditionString")
private String conditionString;
@Column(name = "TimePeriod")
private int timePeriod;
@Column(name = "IsTimePeriodAfter")
private boolean isTimePeriodAfterEvent;
@OneToOne(fetch = FetchType.LAZY, mappedBy = "timeCondition")
private SushiProcess process;
@Transient
private Set<SushiEvent> timerEvents;
@ElementCollection
@CollectionTable(name="TimeCondition_EventAttributes", joinColumns=@JoinColumn(name="Id"))
@MapKeyColumn(name="EventAttributeName", length = 100)
@Column(name="EventAttributeValue", length = 100)
private Map<String, Serializable> attributeExpressionsAndValues = new HashMap<String, Serializable>();
public TimeCondition() {
this.ID = 0;
this.selectedEventType = null;
this.conditionString = null;
this.isTimePeriodAfterEvent = true;
this.timePeriod = 0;
}
/**
* Constructor.
*
* @param eventType
* @param timePeriod in minutes
* @param isTimePeriodAfterEvent
* @param conditionString Pair(s) of attributes and values that narrow down the choice of events to which events from other types can be related to. These events are called timer events.
*/
public TimeCondition(SushiEventType eventType, int timePeriod, boolean isTimePeriodAfterEvent, String conditionString) {
this();
this.selectedEventType = eventType;
this.timePeriod = timePeriod;
this.isTimePeriodAfterEvent = isTimePeriodAfterEvent;
this.conditionString = conditionString;
}
public SushiEventType getSelectedEventType() {
return selectedEventType;
}
public void setSelectedEventType(SushiEventType selectedEventType) {
this.selectedEventType = selectedEventType;
}
public String getConditionString() {
return conditionString;
}
public void setConditionString(String conditionString) {
this.conditionString = conditionString;
}
public int getTimePeriod() {
return timePeriod;
}
public void setTimePeriod(int timePeriod) {
this.timePeriod = timePeriod;
}
public boolean isTimePeriodAfterEvent() {
return isTimePeriodAfterEvent;
}
public void setTimePeriodAfterEvent(boolean isTimePeriodAfterEvent) {
this.isTimePeriodAfterEvent = isTimePeriodAfterEvent;
}
public Set<SushiEvent> getTimerEvents() {
attributeExpressionsAndValues = ConditionParser.extractEventAttributes(conditionString);
timerEvents = new HashSet<SushiEvent>(SushiEvent.findByEventTypeAndAttributeExpressionsAndValues(selectedEventType, attributeExpressionsAndValues));
return timerEvents;
}
/**
* Fetches the timer event to be correlated to from the choice of timer events.
* Timer event and given event must have the same values for the given correlation attributes.
* Timestamp of given event must be in the time period related to the timer event.
*
* @param event the event for which the timer event is searched
* @param correlationAttributes list of single event type attributes
* @return the timer event closest to the given event
*/
public SushiEvent getTimerEventForEvent(SushiEvent event, List<SushiAttribute> correlationAttributes) {
long timerEventTime;
long eventTime = event.getTimestamp().getTime();
long timePeriodInMillis = timePeriod * 60 * 1000;
Map<SushiEvent, Long> possibleTimerEventsAndTimeDifferences = new HashMap<SushiEvent, Long>();
for (SushiEvent timerEvent : getTimerEvents()) {
boolean processInstanceAndEventMatch = true;
for (SushiAttribute actualCorrelationAttribute : correlationAttributes) {
if (!timerEvent.getValues().get(actualCorrelationAttribute.getAttributeExpression()).equals(event.getValues().get(actualCorrelationAttribute.getAttributeExpression()))) {
processInstanceAndEventMatch = false;
break;
}
}
if (processInstanceAndEventMatch) {
timerEventTime = timerEvent.getTimestamp().getTime();
if (isTimePeriodAfterEvent) {
if (timerEventTime <= eventTime && eventTime <= timerEventTime + timePeriodInMillis) {
possibleTimerEventsAndTimeDifferences.put(timerEvent, eventTime - timerEventTime);
}
}
else {
if (timerEventTime - timePeriodInMillis <= eventTime && eventTime <= timerEventTime) {
possibleTimerEventsAndTimeDifferences.put(timerEvent, timerEventTime - eventTime);
}
}
}
}
if (possibleTimerEventsAndTimeDifferences.isEmpty()) {
return null;
} else {
SushiEvent closestTimerEvent = null;
for (SushiEvent timerEvent : possibleTimerEventsAndTimeDifferences.keySet()) {
if (closestTimerEvent == null || possibleTimerEventsAndTimeDifferences.get(timerEvent) < possibleTimerEventsAndTimeDifferences.get(closestTimerEvent)) {
closestTimerEvent = timerEvent;
}
}
return closestTimerEvent;
}
}
/**
* Fetches the timer event to be correlated to from the choice of timer events.
* Timer event and given event must have the same values for the attributes of the given correlation rules.
* Timestamp of given event must be in the time period related to the timer event.
*
* @param event the event for which the timer event is searched
* @param correlationRules set of correlation rules
* @return the timer event closest to the given event
*/
public SushiEvent getTimerEventForEvent(SushiEvent actualEvent, Set<CorrelationRule> correlationRules) {
long timerEventTime;
long eventTime = actualEvent.getTimestamp().getTime();
long timePeriodInMillis = timePeriod * 60 * 1000;
Map<SushiEvent, Long> possibleTimerEventsAndTimeDifferences = new HashMap<SushiEvent, Long>();
for (SushiEvent timerEvent : getTimerEvents()) {
boolean processInstanceAndEventMatch = true;
for (CorrelationRule actualCorrelationRule : correlationRules) {
if (actualCorrelationRule.getFirstAttribute().getEventType().equals(timerEvent.getEventType())
&& actualCorrelationRule.getSecondAttribute().getEventType().equals(actualEvent.getEventType())) {
String attributeExpressionForTimerEvent = actualCorrelationRule.getFirstAttribute().getAttributeExpression();
String attributeExpressionForActualEvent = actualCorrelationRule.getSecondAttribute().getAttributeExpression();
if(!timerEvent.getValues().get(attributeExpressionForTimerEvent).equals(actualEvent.getValues().get(attributeExpressionForActualEvent))) {
processInstanceAndEventMatch = false;
break;
}
}
if (actualCorrelationRule.getSecondAttribute().getEventType().equals(timerEvent.getEventType())
&& actualCorrelationRule.getFirstAttribute().getEventType().equals(actualEvent.getEventType())) {
String attributeExpressionForTimerEvent = actualCorrelationRule.getSecondAttribute().getAttributeExpression();
String attributeExpressionForActualEvent = actualCorrelationRule.getFirstAttribute().getAttributeExpression();
if(!timerEvent.getValues().get(attributeExpressionForTimerEvent).equals(actualEvent.getValues().get(attributeExpressionForActualEvent))) {
processInstanceAndEventMatch = false;
break;
}
}
}
/**
* If there are attributes in the timer event that are used in the correlation rules but not for the determination of timer events,
* a check is required whether its values are equal to the values from the given event.
*/
if (timerEvent.getEventType().equals(actualEvent.getEventType())) {
Set<SushiAttribute> relatedAttributes = new HashSet<SushiAttribute>();
for (CorrelationRule actualCorrelationRule : correlationRules) {
if (actualCorrelationRule.getEventTypeOfFirstAttribute().equals(timerEvent.getEventType())) {
relatedAttributes.add(actualCorrelationRule.getFirstAttribute());
}
if (actualCorrelationRule.getEventTypeOfSecondAttribute().equals(timerEvent.getEventType())) {
relatedAttributes.add(actualCorrelationRule.getSecondAttribute());
}
}
for (SushiAttribute attribute : relatedAttributes) {
String attributeExpression = attribute.getAttributeExpression();
if(!timerEvent.getValues().get(attributeExpression).equals(actualEvent.getValues().get(attributeExpression))) {
processInstanceAndEventMatch = false;
}
}
}
if (processInstanceAndEventMatch) {
timerEventTime = timerEvent.getTimestamp().getTime();
if (isTimePeriodAfterEvent) {
if (timerEventTime <= eventTime && eventTime <= timerEventTime + timePeriodInMillis) {
possibleTimerEventsAndTimeDifferences.put(timerEvent, eventTime - timerEventTime);
}
}
else {
if (timerEventTime - timePeriodInMillis <= eventTime && eventTime <= timerEventTime) {
possibleTimerEventsAndTimeDifferences.put(timerEvent, timerEventTime - eventTime);
}
}
}
}
if (possibleTimerEventsAndTimeDifferences.isEmpty()) {
return null;
} else {
SushiEvent closestTimerEvent = null;
for (SushiEvent timerEvent : possibleTimerEventsAndTimeDifferences.keySet()) {
if (closestTimerEvent == null || possibleTimerEventsAndTimeDifferences.get(timerEvent) < possibleTimerEventsAndTimeDifferences.get(closestTimerEvent)) {
closestTimerEvent = timerEvent;
}
}
return closestTimerEvent;
}
}
public boolean belongsEventToTimerEvent(SushiEvent event, SushiEvent timerEvent) {
long timerEventTime = timerEvent.getTimestamp().getTime();
long eventTime = event.getTimestamp().getTime();
long timePeriodInMillis = timePeriod * 60 * 1000;
if (isTimePeriodAfterEvent) {
return (timerEventTime <= eventTime && eventTime <= timerEventTime + timePeriodInMillis) ? true : false;
} else {
return (timerEventTime - timePeriodInMillis <= eventTime && eventTime <= timerEventTime) ? true : false;
}
}
public int getID() {
return ID;
}
public void setID(int iD) {
ID = iD;
}
@Override
public TimeCondition remove() {
if(process != null){
process.setTimeCondition(null);
process.save();
process = null;
}
return (TimeCondition) super.remove();
}
public void removeTimerEvent(SushiEvent timerEvent){
this.timerEvents.remove(timerEvent);
}
public SushiProcess getProcess() {
return process;
}
public void setProcess(SushiProcess process) {
this.process = process;
}
}