/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you 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 org.wso2.siddhi.query.api;
import org.wso2.siddhi.query.api.annotation.Annotation;
import org.wso2.siddhi.query.api.annotation.Element;
import org.wso2.siddhi.query.api.definition.AbstractDefinition;
import org.wso2.siddhi.query.api.definition.Attribute;
import org.wso2.siddhi.query.api.definition.FunctionDefinition;
import org.wso2.siddhi.query.api.definition.StreamDefinition;
import org.wso2.siddhi.query.api.definition.TableDefinition;
import org.wso2.siddhi.query.api.definition.TriggerDefinition;
import org.wso2.siddhi.query.api.definition.WindowDefinition;
import org.wso2.siddhi.query.api.exception.DuplicateDefinitionException;
import org.wso2.siddhi.query.api.exception.ExecutionPlanValidationException;
import org.wso2.siddhi.query.api.execution.ExecutionElement;
import org.wso2.siddhi.query.api.execution.partition.Partition;
import org.wso2.siddhi.query.api.execution.query.Query;
import org.wso2.siddhi.query.api.util.AnnotationHelper;
import org.wso2.siddhi.query.api.util.SiddhiConstants;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Siddhi execution plan
*/
public class ExecutionPlan {
private Map<String, StreamDefinition> streamDefinitionMap = new HashMap<String, StreamDefinition>();
private Map<String, TableDefinition> tableDefinitionMap = new HashMap<String, TableDefinition>();
private Map<String, WindowDefinition> windowDefinitionMap = new HashMap<String, WindowDefinition>();
private Map<String, TriggerDefinition> triggerDefinitionMap = new HashMap<String, TriggerDefinition>();
private List<ExecutionElement> executionElementList = new ArrayList<ExecutionElement>();
private List<String> executionElementNameList = new ArrayList<String>();
private List<Annotation> annotations = new ArrayList<Annotation>();
private Map<String, FunctionDefinition> functionDefinitionMap = new HashMap<String, FunctionDefinition>();
public ExecutionPlan(String name) {
annotations.add(Annotation.annotation("info").element("name", name));
}
public ExecutionPlan(List<Annotation> annotations) {
this.annotations = annotations;
}
public ExecutionPlan() {
}
public static ExecutionPlan executionPlan(String name) {
return new ExecutionPlan(name);
}
public static ExecutionPlan executionPlan() {
return new ExecutionPlan();
}
public Map<String, FunctionDefinition> getFunctionDefinitionMap() {
return functionDefinitionMap;
}
public ExecutionPlan defineStream(StreamDefinition streamDefinition) {
if (streamDefinition == null) {
throw new ExecutionPlanValidationException("Stream Definition should not be null");
} else if (streamDefinition.getId() == null) {
throw new ExecutionPlanValidationException("Stream Id should not be null for Stream Definition");
}
checkDuplicateDefinition(streamDefinition);
this.streamDefinitionMap.put(streamDefinition.getId(), streamDefinition);
return this;
}
public ExecutionPlan defineTable(TableDefinition tableDefinition) {
if (tableDefinition == null) {
throw new ExecutionPlanValidationException("Table Definition should not be null");
} else if (tableDefinition.getId() == null) {
throw new ExecutionPlanValidationException("Table Id should not be null for Table Definition");
}
checkDuplicateDefinition(tableDefinition);
this.tableDefinitionMap.put(tableDefinition.getId(), tableDefinition);
return this;
}
public ExecutionPlan defineWindow(WindowDefinition windowDefinition) {
if (windowDefinition == null) {
throw new ExecutionPlanValidationException("Window Definition should not be null");
} else if (windowDefinition.getId() == null) {
throw new ExecutionPlanValidationException("Window Id should not be null for Window Definition");
}
checkDuplicateDefinition(windowDefinition);
this.windowDefinitionMap.put(windowDefinition.getId(), windowDefinition);
return this;
}
public ExecutionPlan defineTrigger(TriggerDefinition triggerDefinition) {
if (triggerDefinition == null) {
throw new ExecutionPlanValidationException("Trigger Definition should not be null");
} else if (triggerDefinition.getId() == null) {
throw new ExecutionPlanValidationException("Trigger Id should not be null for Trigger Definition");
}
StreamDefinition streamDefinition = StreamDefinition.id(triggerDefinition.getId()).attribute(SiddhiConstants
.TRIGGERED_TIME, Attribute.Type.LONG);
try {
checkDuplicateDefinition(streamDefinition);
} catch (DuplicateDefinitionException e) {
throw new DuplicateDefinitionException("Trigger '" + triggerDefinition.getId() + "' cannot be defined as," +
" " + e.getMessage(), e);
}
if (triggerDefinitionMap.containsKey(triggerDefinition.getId())) {
throw new DuplicateDefinitionException("Trigger Definition with same Id '" +
triggerDefinition.getId() + "' already exist '" + triggerDefinitionMap.get(triggerDefinition
.getId()) +
"', hence cannot add '" + triggerDefinition + "'");
}
this.triggerDefinitionMap.put(triggerDefinition.getId(), triggerDefinition);
this.streamDefinitionMap.put(streamDefinition.getId(), streamDefinition);
return this;
}
private void checkDuplicateDefinition(AbstractDefinition definition) {
TableDefinition existingTableDefinition = tableDefinitionMap.get(definition.getId());
if (existingTableDefinition != null && (!existingTableDefinition.equals(definition) || definition instanceof
StreamDefinition)) {
throw new DuplicateDefinitionException("Table Definition with same Stream Id '" +
definition.getId() + "' already exist : " + existingTableDefinition +
", hence cannot add " + definition);
}
StreamDefinition existingStreamDefinition = streamDefinitionMap.get(definition.getId());
if (existingStreamDefinition != null && (!existingStreamDefinition.equals(definition) || definition
instanceof TableDefinition)) {
throw new DuplicateDefinitionException("Stream Definition with same Stream Id '" +
definition.getId() + "' already exist : " + existingStreamDefinition +
", hence cannot add " + definition);
}
WindowDefinition existingWindowDefinition = windowDefinitionMap.get(definition.getId());
if (existingWindowDefinition != null && (!existingWindowDefinition.equals(definition) || definition
instanceof WindowDefinition)) {
throw new DuplicateDefinitionException("Stream Definition with same Window Id '" +
definition.getId() + "' already exist : " + existingWindowDefinition +
", hence cannot add " + definition);
}
}
public ExecutionPlan addQuery(Query query) {
if (query == null) {
throw new ExecutionPlanValidationException("Query should not be null");
}
String name = null;
Element element = AnnotationHelper.getAnnotationElement(SiddhiConstants.ANNOTATION_INFO, SiddhiConstants
.ANNOTATION_ELEMENT_NAME, query.getAnnotations());
if (element != null) {
name = element.getValue();
}
if (name != null && executionElementNameList.contains(name)) {
throw new ExecutionPlanValidationException("Cannot add Query as another Execution Element already uses " +
"its name=" + name);
}
executionElementNameList.add(name);
this.executionElementList.add(query);
return this;
}
public ExecutionPlan addPartition(Partition partition) {
if (partition == null) {
throw new ExecutionPlanValidationException("Partition should not be null");
}
String name = null;
Element element = AnnotationHelper.getAnnotationElement(SiddhiConstants.ANNOTATION_INFO, SiddhiConstants
.ANNOTATION_ELEMENT_NAME, partition.getAnnotations());
if (element != null) {
name = element.getValue();
}
if (name != null && executionElementNameList.contains(name)) {
throw new ExecutionPlanValidationException("Cannot add Partition as another Execution Element already " +
"uses its name=" + name);
}
executionElementNameList.add(name);
this.executionElementList.add(partition);
return this;
}
public ExecutionPlan annotation(Annotation annotation) {
annotations.add(annotation);
return this;
}
public List<Annotation> getAnnotations() {
return annotations;
}
public List<ExecutionElement> getExecutionElementList() {
return executionElementList;
}
public Map<String, StreamDefinition> getStreamDefinitionMap() {
return streamDefinitionMap;
}
public Map<String, TableDefinition> getTableDefinitionMap() {
return tableDefinitionMap;
}
public Map<String, TriggerDefinition> getTriggerDefinitionMap() {
return triggerDefinitionMap;
}
public Map<String, WindowDefinition> getWindowDefinitionMap() {
return windowDefinitionMap;
}
@Override
public String toString() {
return "ExecutionPlan{" +
"streamDefinitionMap=" + streamDefinitionMap +
", tableDefinitionMap=" + tableDefinitionMap +
", windowDefinitionMap=" + windowDefinitionMap +
", executionElementList=" + executionElementList +
", executionElementNameList=" + executionElementNameList +
", annotations=" + annotations +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ExecutionPlan)) {
return false;
}
ExecutionPlan that = (ExecutionPlan) o;
if (annotations != null ? !annotations.equals(that.annotations) : that.annotations != null) {
return false;
}
if (executionElementList != null ? !executionElementList.equals(that.executionElementList) : that
.executionElementList != null) {
return false;
}
if (executionElementNameList != null ? !executionElementNameList.equals(that.executionElementNameList) : that
.executionElementNameList != null) {
return false;
}
if (streamDefinitionMap != null ? !streamDefinitionMap.equals(that.streamDefinitionMap) : that
.streamDefinitionMap != null) {
return false;
}
if (tableDefinitionMap != null ? !tableDefinitionMap.equals(that.tableDefinitionMap) : that
.tableDefinitionMap != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = streamDefinitionMap != null ? streamDefinitionMap.hashCode() : 0;
result = 31 * result + (tableDefinitionMap != null ? tableDefinitionMap.hashCode() : 0);
result = 31 * result + (executionElementList != null ? executionElementList.hashCode() : 0);
result = 31 * result + (executionElementNameList != null ? executionElementNameList.hashCode() : 0);
result = 31 * result + (annotations != null ? annotations.hashCode() : 0);
return result;
}
public void defineFunction(FunctionDefinition functionDefinition) {
if (functionDefinition == null) {
throw new ExecutionPlanValidationException("Function Definition should not be null");
} else if (functionDefinition.getId() == null) {
throw new ExecutionPlanValidationException("Function Id should not be null for Function Definition");
} else if (functionDefinition.getReturnType() == null) {
throw new ExecutionPlanValidationException("Return type should not be null for Function Definition");
} else if (functionDefinition.getBody() == null) {
throw new ExecutionPlanValidationException("Body should not be null for Function Definition");
} else if (functionDefinition.getLanguage() == null) {
throw new ExecutionPlanValidationException("Language should not be null for Function Definition");
}
checkDuplicateFunctionExist(functionDefinition);
this.functionDefinitionMap.put(functionDefinition.getId(), functionDefinition);
}
private void checkDuplicateFunctionExist(FunctionDefinition functionDefinition) {
if (this.functionDefinitionMap.get(functionDefinition.getId()) != null) {
throw new DuplicateDefinitionException("The function definition with the same id exists " +
functionDefinition.getId());
}
}
}