blob: 5819f117d29854b7dfca7a9e620a60f4c203561b [file] [log] [blame]
jaegonkim6a7b5242018-09-12 23:09:42 +09001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.workflow.api;
17
18import com.google.common.base.MoreObjects;
19import com.google.common.collect.ImmutableList;
20import com.google.common.collect.ImmutableSet;
21import com.google.common.collect.Lists;
22import com.google.common.collect.Sets;
23import org.onlab.osgi.DefaultServiceDirectory;
24import org.onlab.osgi.ServiceNotFoundException;
25
26import java.lang.reflect.Modifier;
27import java.net.URI;
jaegonkim4064ed12019-05-18 11:34:45 +090028import java.util.ArrayList;
jaegonkim6a7b5242018-09-12 23:09:42 +090029import java.util.List;
30import java.util.Objects;
31import java.util.Set;
32
jaegonkime0f45b52018-10-09 20:23:26 +090033import static org.onosproject.workflow.api.CheckCondition.check;
34
jaegonkim6a7b5242018-09-12 23:09:42 +090035/**
36 * Class for immutable list workflow.
37 */
38public final class ImmutableListWorkflow extends AbstractWorkflow {
39
40 /**
41 * Init worklet type(class name of init worklet type).
42 */
43 private String initWorkletType;
44
45 /**
jaegonkim4064ed12019-05-18 11:34:45 +090046 * Sequential program(List of worklet desc) to be executed.
jaegonkim6a7b5242018-09-12 23:09:42 +090047 */
jaegonkim4064ed12019-05-18 11:34:45 +090048 private List<WorkletDescription> program;
jaegonkim6a7b5242018-09-12 23:09:42 +090049
50 /**
51 * Set of workflow attributes.
52 */
53 private Set<WorkflowAttribute> attributes;
54
jaegonkime0f45b52018-10-09 20:23:26 +090055 private static JsonDataModelInjector dataModelInjector = new JsonDataModelInjector();
m.rahil09251882019-04-15 22:58:33 +053056 private static StaticDataModelInjector staticDataModelInjector = new StaticDataModelInjector();
jaegonkime0f45b52018-10-09 20:23:26 +090057
jaegonkim6a7b5242018-09-12 23:09:42 +090058 /**
59 * Constructor of ImmutableListWorkflow.
mohamedrahilr63a921c2019-02-27 19:48:25 +053060 *
jaegonkim6a7b5242018-09-12 23:09:42 +090061 * @param builder builder of ImmutableListWorkflow
62 */
63 private ImmutableListWorkflow(Builder builder) {
64 super(builder.id);
65 this.initWorkletType = builder.initWorkletType;
jaegonkim4064ed12019-05-18 11:34:45 +090066 program = ImmutableList.copyOf(builder.workletDescList);
jaegonkim6a7b5242018-09-12 23:09:42 +090067 attributes = ImmutableSet.copyOf(builder.attributes);
68 }
69
70 @Override
71 public Worklet init(WorkflowContext context) throws WorkflowException {
72 if (Objects.isNull(initWorkletType)) {
73 return null;
74 }
75
76 return getWorkletInstance(initWorkletType);
77 }
78
jaegonkim6a7b5242018-09-12 23:09:42 +090079 @Override
jaegonkime0f45b52018-10-09 20:23:26 +090080 public ProgramCounter next(WorkflowContext context) throws WorkflowException {
jaegonkim6a7b5242018-09-12 23:09:42 +090081
82 int cnt = 0;
jaegonkime0f45b52018-10-09 20:23:26 +090083
jaegonkimf85ee3c2019-04-21 11:10:25 +090084 ProgramCounter current = context.current();
85 check(current != null, "Invalid program counter");
jaegonkime0f45b52018-10-09 20:23:26 +090086
jaegonkimf85ee3c2019-04-21 11:10:25 +090087 ProgramCounter pc = current.clone();
88
jaegonkim4064ed12019-05-18 11:34:45 +090089 for (int i = current.workletIndex(); i < program.size(); pc = increased(pc), i++) {
jaegonkim6a7b5242018-09-12 23:09:42 +090090
91 if (cnt++ > Worklet.MAX_WORKS) {
92 throw new WorkflowException("Maximum worklet execution exceeded");
93 }
jaegonkimf85ee3c2019-04-21 11:10:25 +090094 if (pc.isCompleted()) {
95 return pc;
jaegonkim6a7b5242018-09-12 23:09:42 +090096 }
97
jaegonkimf85ee3c2019-04-21 11:10:25 +090098 if (pc.isInit()) {
jaegonkim6a7b5242018-09-12 23:09:42 +090099 continue;
100 }
101
jaegonkimf85ee3c2019-04-21 11:10:25 +0900102 Worklet worklet = getWorkletInstance(pc);
jaegonkim6a7b5242018-09-12 23:09:42 +0900103 Class workClass = worklet.getClass();
104
105 if (BranchWorklet.class.isAssignableFrom(workClass)) {
106 Class nextClass = ((BranchWorklet) worklet).next(context);
107 if (nextClass == null) {
108 throw new WorkflowException("Invalid next Worklet for " + workClass);
109 }
110
111 // TODO : it does not support duplicated use of WorkType. It needs to consider label concept
112 int nextIdx = getClassIndex(nextClass);
113 if (nextIdx == -1) {
114 throw new WorkflowException("Failed to find next " + nextClass + " for " + workClass);
115 }
116
117 i = nextIdx;
118 continue;
119
120 } else {
jaegonkime0f45b52018-10-09 20:23:26 +0900121 // isNext is read only. It does not perform 'inhale'.
122 dataModelInjector.inject(worklet, context);
m.rahil09251882019-04-15 22:58:33 +0530123 WorkletDescription workletDesc = getWorkletDesc(pc);
124 if (Objects.nonNull(workletDesc)) {
125 if (!(workletDesc.tag().equals("INIT") || workletDesc.tag().equals("COMPLETED"))) {
126 staticDataModelInjector.inject(worklet, workletDesc);
127 }
128 }
jaegonkim6a7b5242018-09-12 23:09:42 +0900129 if (worklet.isNext(context)) {
jaegonkimf85ee3c2019-04-21 11:10:25 +0900130 return pc;
jaegonkim6a7b5242018-09-12 23:09:42 +0900131 }
132 }
133 }
jaegonkimf85ee3c2019-04-21 11:10:25 +0900134 throw new WorkflowException("workflow reached to end but not COMPLETED (pc:"
135 + current + ", workflow:" + this.toString());
jaegonkime0f45b52018-10-09 20:23:26 +0900136 }
137
138 @Override
139 public ProgramCounter increased(ProgramCounter pc) throws WorkflowException {
140
141 int increaedIndex = pc.workletIndex() + 1;
jaegonkim4064ed12019-05-18 11:34:45 +0900142 if (increaedIndex >= program.size()) {
jaegonkime0f45b52018-10-09 20:23:26 +0900143 throw new WorkflowException("Out of bound in program counter(" + pc + ")");
144 }
145
jaegonkim4064ed12019-05-18 11:34:45 +0900146 WorkletDescription workletDesc = program.get(increaedIndex);
m.rahil09251882019-04-15 22:58:33 +0530147 return ProgramCounter.valueOf(workletDesc.tag(), increaedIndex);
jaegonkim6a7b5242018-09-12 23:09:42 +0900148 }
149
150 @Override
jaegonkimf85ee3c2019-04-21 11:10:25 +0900151 public Worklet getWorkletInstance(ProgramCounter pc) throws WorkflowException {
152
jaegonkim4064ed12019-05-18 11:34:45 +0900153 return getWorkletInstance(program.get(pc.workletIndex()).tag());
jaegonkimf85ee3c2019-04-21 11:10:25 +0900154 }
155
jaegonkim4064ed12019-05-18 11:34:45 +0900156 private Worklet getWorkletInstance(String workletType) throws WorkflowException {
jaegonkim6a7b5242018-09-12 23:09:42 +0900157
jaegonkim13b25cb2019-03-22 06:23:23 +0900158 if (Worklet.Common.INIT.tag().equals(workletType)) {
159 return Worklet.Common.INIT;
160 }
161
jaegonkime0f45b52018-10-09 20:23:26 +0900162 if (Worklet.Common.COMPLETED.tag().equals(workletType)) {
163 return Worklet.Common.COMPLETED;
164 }
165
jaegonkim6a7b5242018-09-12 23:09:42 +0900166 WorkflowStore store;
167 try {
mohamedrahilr63a921c2019-02-27 19:48:25 +0530168 store = DefaultServiceDirectory.getService(WorkflowStore.class);
jaegonkim6a7b5242018-09-12 23:09:42 +0900169 } catch (ServiceNotFoundException e) {
170 throw new WorkflowException(e);
171 }
172
173 Class workClass;
174 try {
175 workClass = store.getClass(workletType);
176 } catch (ClassNotFoundException e) {
177 throw new WorkflowException(e);
178 }
179
180 if (!isAllowed(workClass)) {
181 throw new WorkflowException("Not allowed class(" + workClass.getSimpleName() + ")");
182 }
183
184 Worklet worklet;
185 try {
186 worklet = (Worklet) workClass.newInstance();
187 } catch (Exception e) {
188 throw new WorkflowException(e);
189 }
190
191 return worklet;
192 }
193
194 @Override
195 public Set<WorkflowAttribute> attributes() {
196 return ImmutableSet.copyOf(attributes);
197 }
198
mohamedrahilr63a921c2019-02-27 19:48:25 +0530199 @Override
jaegonkim4064ed12019-05-18 11:34:45 +0900200 public List<ProgramCounter> getProgram() {
201
202 int size = program.size();
203 List pcList = new ArrayList();
204 for (int i = 0; i < size; i++) {
205 pcList.add(ProgramCounter.valueOf(program.get(i).tag(), i));
206 }
207
208 return pcList;
mohamedrahilr63a921c2019-02-27 19:48:25 +0530209 }
210
m.rahil09251882019-04-15 22:58:33 +0530211 @Override
212 public WorkletDescription getWorkletDesc(ProgramCounter pc) {
m.rahilfaf04d72019-05-24 21:11:22 +0530213
214 WorkletDescription workletDescription = program.get(pc.workletIndex());
215 return workletDescription;
m.rahil09251882019-04-15 22:58:33 +0530216 }
217
218
jaegonkim6a7b5242018-09-12 23:09:42 +0900219 /**
220 * Gets index of class in worklet type list.
mohamedrahilr63a921c2019-02-27 19:48:25 +0530221 *
jaegonkim6a7b5242018-09-12 23:09:42 +0900222 * @param aClass class to get index
223 * @return index of class in worklet type list
224 */
225 private int getClassIndex(Class aClass) {
jaegonkim4064ed12019-05-18 11:34:45 +0900226 for (int i = 0; i < program.size(); i++) {
227 if (Objects.equals(aClass.getName(), program.get(i))) {
jaegonkim6a7b5242018-09-12 23:09:42 +0900228 return i;
229 }
230 }
231 return -1;
232 }
233
234 /**
235 * Checks whether class is allowed class or not.
mohamedrahilr63a921c2019-02-27 19:48:25 +0530236 *
jaegonkim6a7b5242018-09-12 23:09:42 +0900237 * @param clazz class to check
238 * @return Check result
239 */
240 private boolean isAllowed(Class clazz) {
241 // non static inner class is not allowed
242 if (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers())) {
243 return false;
244 }
245 // enum is not allowed
246 if (clazz.isEnum()) {
247 return false;
248 }
249 // class should be subclass of Work
250 if (!Worklet.class.isAssignableFrom(clazz)) {
251 return false;
252 }
253 return true;
254 }
255
256 @Override
257 public int hashCode() {
258 return Objects.hash(this.toString());
259 }
260
261 @Override
262 public boolean equals(Object obj) {
263 if (obj == this) {
264 return true;
265 }
266 if (!(obj instanceof EventTask)) {
267 return false;
268 }
269 return Objects.equals(this.id(), ((ImmutableListWorkflow) obj).id())
270 && Objects.equals(this.initWorkletType, ((ImmutableListWorkflow) obj).initWorkletType)
jaegonkim4064ed12019-05-18 11:34:45 +0900271 && Objects.equals(this.program, ((ImmutableListWorkflow) obj).program)
jaegonkim6a7b5242018-09-12 23:09:42 +0900272 && Objects.equals(this.attributes, ((ImmutableListWorkflow) obj).attributes);
273 }
274
275 @Override
276 public String toString() {
277 return MoreObjects.toStringHelper(getClass())
278 .add("id", id())
279 .add("initWorklet", initWorkletType)
jaegonkim4064ed12019-05-18 11:34:45 +0900280 .add("workList", program)
jaegonkim6a7b5242018-09-12 23:09:42 +0900281 .add("attributes", attributes)
282 .toString();
283 }
284
285 /**
286 * Gets a instance of builder.
mohamedrahilr63a921c2019-02-27 19:48:25 +0530287 *
jaegonkim6a7b5242018-09-12 23:09:42 +0900288 * @return instance of builder
289 */
290 public static Builder builder() {
291 return new Builder();
292 }
293
294 /**
295 * Builder of ImmutableListWorkflow.
296 */
297 public static class Builder {
298
299 private URI id;
300 private String initWorkletType;
m.rahil09251882019-04-15 22:58:33 +0530301 private final List<WorkletDescription> workletDescList = Lists.newArrayList();
jaegonkim6a7b5242018-09-12 23:09:42 +0900302 private final Set<WorkflowAttribute> attributes = Sets.newHashSet();
303
304 /**
305 * Sets id of immutable list workflow.
mohamedrahilr63a921c2019-02-27 19:48:25 +0530306 *
jaegonkim6a7b5242018-09-12 23:09:42 +0900307 * @param uri id of immutable list workflow
308 * @return builder
309 */
310 public Builder id(URI uri) {
311 this.id = uri;
m.rahil09251882019-04-15 22:58:33 +0530312 workletDescList.add(new DefaultWorkletDescription(Worklet.Common.INIT.tag()));
jaegonkim6a7b5242018-09-12 23:09:42 +0900313 return this;
314 }
315
316 /**
317 * Sets init worklet class name of immutable list workflow.
mohamedrahilr63a921c2019-02-27 19:48:25 +0530318 *
jaegonkim6a7b5242018-09-12 23:09:42 +0900319 * @param workletClassName class name of worklet
320 * @return builder
321 */
322 public Builder init(String workletClassName) {
323 this.initWorkletType = workletClassName;
324 return this;
325 }
326
327 /**
328 * Chains worklet class name of immutable list workflow.
mohamedrahilr63a921c2019-02-27 19:48:25 +0530329 *
jaegonkim6a7b5242018-09-12 23:09:42 +0900330 * @param workletClassName class name of worklet
331 * @return builder
332 */
333 public Builder chain(String workletClassName) {
m.rahil09251882019-04-15 22:58:33 +0530334 workletDescList.add(new DefaultWorkletDescription(workletClassName));
jaegonkim6a7b5242018-09-12 23:09:42 +0900335 return this;
336 }
337
338 /**
339 * Adds workflow attribute.
mohamedrahilr63a921c2019-02-27 19:48:25 +0530340 *
jaegonkim6a7b5242018-09-12 23:09:42 +0900341 * @param attribute workflow attribute to be added
342 * @return builder
343 */
344 public Builder attribute(WorkflowAttribute attribute) {
345 attributes.add(attribute);
346 return this;
347 }
348
349 /**
350 * Builds ImmutableListWorkflow.
mohamedrahilr63a921c2019-02-27 19:48:25 +0530351 *
jaegonkim6a7b5242018-09-12 23:09:42 +0900352 * @return instance of ImmutableListWorkflow
353 */
354 public ImmutableListWorkflow build() {
m.rahil09251882019-04-15 22:58:33 +0530355 workletDescList.add(new DefaultWorkletDescription(Worklet.Common.COMPLETED.tag()));
jaegonkim6a7b5242018-09-12 23:09:42 +0900356 return new ImmutableListWorkflow(this);
357 }
m.rahil09251882019-04-15 22:58:33 +0530358
359 public Builder chain(DefaultWorkletDescription workletDesc) {
360 workletDescList.add(workletDesc);
361 return this;
362 }
jaegonkim6a7b5242018-09-12 23:09:42 +0900363 }
364}