| /* |
| * Copyright 2018-present Open Networking Foundation |
| * |
| * 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 org.onosproject.workflow.api; |
| |
| import com.google.common.base.MoreObjects; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Sets; |
| import org.onlab.osgi.DefaultServiceDirectory; |
| import org.onlab.osgi.ServiceNotFoundException; |
| |
| import java.lang.reflect.Modifier; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.Set; |
| |
| import static org.onosproject.workflow.api.CheckCondition.check; |
| |
| /** |
| * Class for immutable list workflow. |
| */ |
| public final class ImmutableListWorkflow extends AbstractWorkflow { |
| |
| /** |
| * Init worklet type(class name of init worklet type). |
| */ |
| private String initWorkletType; |
| |
| /** |
| * Sequential program(List of worklet desc) to be executed. |
| */ |
| private List<WorkletDescription> program; |
| |
| /** |
| * Set of workflow attributes. |
| */ |
| private Set<WorkflowAttribute> attributes; |
| |
| private static JsonDataModelInjector dataModelInjector = new JsonDataModelInjector(); |
| private static StaticDataModelInjector staticDataModelInjector = new StaticDataModelInjector(); |
| |
| /** |
| * Constructor of ImmutableListWorkflow. |
| * |
| * @param builder builder of ImmutableListWorkflow |
| */ |
| private ImmutableListWorkflow(Builder builder) { |
| super(builder.id); |
| this.initWorkletType = builder.initWorkletType; |
| program = ImmutableList.copyOf(builder.workletDescList); |
| attributes = ImmutableSet.copyOf(builder.attributes); |
| } |
| |
| @Override |
| public Worklet init(WorkflowContext context) throws WorkflowException { |
| if (Objects.isNull(initWorkletType)) { |
| return null; |
| } |
| |
| return getWorkletInstance(initWorkletType); |
| } |
| |
| @Override |
| public ProgramCounter next(WorkflowContext context) throws WorkflowException { |
| |
| int cnt = 0; |
| |
| ProgramCounter current = context.current(); |
| check(current != null, "Invalid program counter"); |
| |
| ProgramCounter pc = current.clone(); |
| |
| for (int i = current.workletIndex(); i < program.size(); pc = increased(pc), i++) { |
| |
| if (cnt++ > Worklet.MAX_WORKS) { |
| throw new WorkflowException("Maximum worklet execution exceeded"); |
| } |
| if (pc.isCompleted()) { |
| return pc; |
| } |
| |
| if (pc.isInit()) { |
| continue; |
| } |
| |
| Worklet worklet = getWorkletInstance(pc); |
| Class workClass = worklet.getClass(); |
| |
| if (BranchWorklet.class.isAssignableFrom(workClass)) { |
| Class nextClass = ((BranchWorklet) worklet).next(context); |
| if (nextClass == null) { |
| throw new WorkflowException("Invalid next Worklet for " + workClass); |
| } |
| |
| // TODO : it does not support duplicated use of WorkType. It needs to consider label concept |
| int nextIdx = getClassIndex(nextClass); |
| if (nextIdx == -1) { |
| throw new WorkflowException("Failed to find next " + nextClass + " for " + workClass); |
| } |
| |
| i = nextIdx; |
| continue; |
| |
| } else { |
| // isNext is read only. It does not perform 'inhale'. |
| dataModelInjector.inject(worklet, context); |
| WorkletDescription workletDesc = getWorkletDesc(pc); |
| if (Objects.nonNull(workletDesc)) { |
| if (!(workletDesc.tag().equals("INIT") || workletDesc.tag().equals("COMPLETED"))) { |
| staticDataModelInjector.inject(worklet, workletDesc); |
| } |
| } |
| if (worklet.isNext(context)) { |
| return pc; |
| } |
| } |
| } |
| throw new WorkflowException("workflow reached to end but not COMPLETED (pc:" |
| + current + ", workflow:" + this.toString()); |
| } |
| |
| @Override |
| public ProgramCounter increased(ProgramCounter pc) throws WorkflowException { |
| |
| int increaedIndex = pc.workletIndex() + 1; |
| if (increaedIndex >= program.size()) { |
| throw new WorkflowException("Out of bound in program counter(" + pc + ")"); |
| } |
| |
| WorkletDescription workletDesc = program.get(increaedIndex); |
| return ProgramCounter.valueOf(workletDesc.tag(), increaedIndex); |
| } |
| |
| @Override |
| public Worklet getWorkletInstance(ProgramCounter pc) throws WorkflowException { |
| |
| return getWorkletInstance(program.get(pc.workletIndex()).tag()); |
| } |
| |
| private Worklet getWorkletInstance(String workletType) throws WorkflowException { |
| |
| if (Worklet.Common.INIT.tag().equals(workletType)) { |
| return Worklet.Common.INIT; |
| } |
| |
| if (Worklet.Common.COMPLETED.tag().equals(workletType)) { |
| return Worklet.Common.COMPLETED; |
| } |
| |
| WorkflowStore store; |
| try { |
| store = DefaultServiceDirectory.getService(WorkflowStore.class); |
| } catch (ServiceNotFoundException e) { |
| throw new WorkflowException(e); |
| } |
| |
| Class workClass; |
| try { |
| workClass = store.getClass(workletType); |
| } catch (ClassNotFoundException e) { |
| throw new WorkflowException(e); |
| } |
| |
| if (!isAllowed(workClass)) { |
| throw new WorkflowException("Not allowed class(" + workClass.getSimpleName() + ")"); |
| } |
| |
| Worklet worklet; |
| try { |
| worklet = (Worklet) workClass.newInstance(); |
| } catch (Exception e) { |
| throw new WorkflowException(e); |
| } |
| |
| return worklet; |
| } |
| |
| @Override |
| public Set<WorkflowAttribute> attributes() { |
| return ImmutableSet.copyOf(attributes); |
| } |
| |
| @Override |
| public List<ProgramCounter> getProgram() { |
| |
| int size = program.size(); |
| List pcList = new ArrayList(); |
| for (int i = 0; i < size; i++) { |
| pcList.add(ProgramCounter.valueOf(program.get(i).tag(), i)); |
| } |
| |
| return pcList; |
| } |
| |
| @Override |
| public WorkletDescription getWorkletDesc(ProgramCounter pc) { |
| |
| WorkletDescription workletDescription = program.get(pc.workletIndex()); |
| return workletDescription; |
| } |
| |
| |
| /** |
| * Gets index of class in worklet type list. |
| * |
| * @param aClass class to get index |
| * @return index of class in worklet type list |
| */ |
| private int getClassIndex(Class aClass) { |
| for (int i = 0; i < program.size(); i++) { |
| if (Objects.equals(aClass.getName(), program.get(i))) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Checks whether class is allowed class or not. |
| * |
| * @param clazz class to check |
| * @return Check result |
| */ |
| private boolean isAllowed(Class clazz) { |
| // non static inner class is not allowed |
| if (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers())) { |
| return false; |
| } |
| // enum is not allowed |
| if (clazz.isEnum()) { |
| return false; |
| } |
| // class should be subclass of Work |
| if (!Worklet.class.isAssignableFrom(clazz)) { |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(this.toString()); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj == this) { |
| return true; |
| } |
| if (!(obj instanceof EventTask)) { |
| return false; |
| } |
| return Objects.equals(this.id(), ((ImmutableListWorkflow) obj).id()) |
| && Objects.equals(this.initWorkletType, ((ImmutableListWorkflow) obj).initWorkletType) |
| && Objects.equals(this.program, ((ImmutableListWorkflow) obj).program) |
| && Objects.equals(this.attributes, ((ImmutableListWorkflow) obj).attributes); |
| } |
| |
| @Override |
| public String toString() { |
| return MoreObjects.toStringHelper(getClass()) |
| .add("id", id()) |
| .add("initWorklet", initWorkletType) |
| .add("workList", program) |
| .add("attributes", attributes) |
| .toString(); |
| } |
| |
| /** |
| * Gets a instance of builder. |
| * |
| * @return instance of builder |
| */ |
| public static Builder builder() { |
| return new Builder(); |
| } |
| |
| /** |
| * Builder of ImmutableListWorkflow. |
| */ |
| public static class Builder { |
| |
| private URI id; |
| private String initWorkletType; |
| private final List<WorkletDescription> workletDescList = Lists.newArrayList(); |
| private final Set<WorkflowAttribute> attributes = Sets.newHashSet(); |
| |
| /** |
| * Sets id of immutable list workflow. |
| * |
| * @param uri id of immutable list workflow |
| * @return builder |
| */ |
| public Builder id(URI uri) { |
| this.id = uri; |
| workletDescList.add(new DefaultWorkletDescription(Worklet.Common.INIT.tag())); |
| return this; |
| } |
| |
| /** |
| * Sets init worklet class name of immutable list workflow. |
| * |
| * @param workletClassName class name of worklet |
| * @return builder |
| */ |
| public Builder init(String workletClassName) { |
| this.initWorkletType = workletClassName; |
| return this; |
| } |
| |
| /** |
| * Chains worklet class name of immutable list workflow. |
| * |
| * @param workletClassName class name of worklet |
| * @return builder |
| */ |
| public Builder chain(String workletClassName) { |
| workletDescList.add(new DefaultWorkletDescription(workletClassName)); |
| return this; |
| } |
| |
| /** |
| * Adds workflow attribute. |
| * |
| * @param attribute workflow attribute to be added |
| * @return builder |
| */ |
| public Builder attribute(WorkflowAttribute attribute) { |
| attributes.add(attribute); |
| return this; |
| } |
| |
| /** |
| * Builds ImmutableListWorkflow. |
| * |
| * @return instance of ImmutableListWorkflow |
| */ |
| public ImmutableListWorkflow build() { |
| workletDescList.add(new DefaultWorkletDescription(Worklet.Common.COMPLETED.tag())); |
| return new ImmutableListWorkflow(this); |
| } |
| |
| public Builder chain(DefaultWorkletDescription workletDesc) { |
| workletDescList.add(workletDesc); |
| return this; |
| } |
| } |
| } |