[ONOS-7732] Automating switch workflow: annotation based data model injection, applying programming counter, and small fixes

Change-Id: I4092d9c2695bcc8c4e8e01d54c442d3fac284eb6
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/AbstractWorkflow.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/AbstractWorkflow.java
index 8a5f2b8..5fb8943 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/AbstractWorkflow.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/AbstractWorkflow.java
@@ -42,11 +42,19 @@
 
     @Override
     public WorkflowContext buildContext(Workplace workplace, DataModelTree data) throws WorkflowException {
-        return new DefaultWorkflowContext(id, workplace.name(), data);
+        return DefaultWorkflowContext.builder()
+                .workflowId(id)
+                .workplaceName(workplace.name())
+                .data(data)
+                .build();
     }
 
     @Override
     public WorkflowContext buildSystemContext(Workplace workplace, DataModelTree data) throws WorkflowException {
-        return new SystemWorkflowContext(id, workplace.name(), data);
+        return SystemWorkflowContext.systemBuilder()
+                .workflowId(id)
+                .workplaceName(workplace.name())
+                .data(data)
+                .build();
     }
 }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/CheckCondition.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/CheckCondition.java
new file mode 100644
index 0000000..7419583
--- /dev/null
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/CheckCondition.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+
+/**
+ * Static convenience class that help a whether it was invoked correctly(whether its preconditions have been met).
+ * Methods of this class generally accept a boolean expression which is expected to be true.
+ * When false (or null) is passed instead, the Preconditions method throws an workflow exception.
+ */
+public final class CheckCondition {
+
+    /**
+     * Private class of check condition.
+     */
+    private CheckCondition() {
+    }
+
+    /**
+     * Checks the condition, and if it is false, it raises workflow exception with exception message.
+     * @param condition condition to check. A boolean expression is located on here.
+     * @param exceptionMessage exception message for workflow exception
+     * @throws WorkflowException workflow exception
+     */
+    public static void check(boolean condition, String exceptionMessage) throws WorkflowException {
+        if (!condition) {
+            throw new WorkflowException(exceptionMessage);
+        }
+    }
+
+}
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ContextEventMapStore.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ContextEventMapStore.java
index c26b3be..6d0f0fb 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ContextEventMapStore.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ContextEventMapStore.java
@@ -32,11 +32,11 @@
      * @param eventType the class name of event
      * @param eventHint event hint string value of the event
      * @param contextName workflow context name
-     * @param workletType the class name of worklet
+     * @param programCounterString the program counter of workflow
      * @throws WorkflowException workflow exception
      */
     void registerEventMap(String eventType, String eventHint,
-                          String contextName, String workletType) throws WorkflowException;
+                          String contextName, String programCounterString) throws WorkflowException;
 
     /**
      * Unregisters workflow context event mapping.
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DataModelTree.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DataModelTree.java
index 3236164..f7ada8d 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DataModelTree.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DataModelTree.java
@@ -59,5 +59,12 @@
      * @throws WorkflowException workflow exception
      */
     DataModelTree alloc(String path, Nodetype leaftype) throws WorkflowException;
+
+    /**
+     * Remove node on the path.
+     * @param path data model tree path
+     * @throws WorkflowException workflow exception
+     */
+    void remove(String path) throws WorkflowException;
 }
 
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultRpcDescription.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultRpcDescription.java
index 4a4715e..6f9d3c3 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultRpcDescription.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultRpcDescription.java
@@ -20,6 +20,8 @@
 import com.fasterxml.jackson.databind.node.TextNode;
 import com.google.common.base.MoreObjects;
 
+import static org.onosproject.workflow.api.CheckCondition.check;
+
 /**
  * Class of workflow RPC description.
  */
@@ -169,8 +171,12 @@
         /**
          * Builds workplace RPC description from builder.
          * @return instance of workflow RPC description
+         * @throws WorkflowException workflow exception
          */
-        public DefaultRpcDescription build() {
+        public DefaultRpcDescription build() throws WorkflowException {
+            check(op != null, "op is invalid");
+            check(params != null, "params is invalid");
+            check(id != null, "id is invalid");
             return new DefaultRpcDescription(this);
         }
     }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkflowContext.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkflowContext.java
index e16376f..05c564a 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkflowContext.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkflowContext.java
@@ -16,10 +16,14 @@
 package org.onosproject.workflow.api;
 
 import com.google.common.base.MoreObjects;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceNotFoundException;
 import org.onosproject.event.Event;
 
 import java.net.URI;
 
+import static org.onosproject.workflow.api.CheckCondition.check;
+
 /**
  * Default implementation of WorkflowContext.
  */
@@ -41,9 +45,9 @@
     private WorkflowState state;
 
     /**
-     * Current worklet of the workflow.
+     * Current program counter of the workflow.
      */
-    private String current;
+    private ProgramCounter current;
 
     /**
      * Cause of workflow exception.
@@ -87,16 +91,14 @@
 
     /**
      * Constructor of DefaultWorkflowContext.
-     * @param workflowId ID of workflow
-     * @param workplaceName name of workplace
-     * @param data data model tree
+     * @param builder default workflow context builder
      */
-    public DefaultWorkflowContext(URI workflowId, String workplaceName, DataModelTree data) {
-        super(data);
-        this.workflowId = workflowId;
-        this.workplaceName = workplaceName;
+    protected DefaultWorkflowContext(Builder builder) {
+        super(builder.data);
+        this.workflowId = builder.workflowId;
+        this.workplaceName = builder.workplaceName;
         this.state = WorkflowState.IDLE;
-        this.current = Worklet.Common.INIT.name();
+        this.current = ProgramCounter.INIT_PC;
     }
 
     /**
@@ -140,13 +142,13 @@
     }
 
     @Override
-    public String current() {
+    public ProgramCounter current() {
         return this.current;
     }
 
     @Override
-    public void setCurrent(Worklet worklet) {
-        this.current = worklet.tag();
+    public void setCurrent(ProgramCounter pc) {
+        this.current = pc;
     }
 
     @Override
@@ -228,6 +230,16 @@
         return workplaceStore;
     }
 
+    public <T> T getService(Class<T> serviceClass) throws WorkflowException {
+        T service;
+        try {
+            service = DefaultServiceDirectory.getService(serviceClass);
+        } catch (ServiceNotFoundException e) {
+            throw new WorkflowException(e);
+        }
+        return service;
+    }
+
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
@@ -239,4 +251,75 @@
                 .add("cause", cause())
                 .toString();
     }
+
+    /**
+     * Gets builder instance.
+     * @return builder instance
+     */
+    public static final Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for default workflow context.
+     */
+    public static class Builder {
+
+        /**
+         * ID of workflow.
+         */
+        private URI workflowId;
+
+        /**
+         * Workplace name of the workflow.
+         */
+        private String workplaceName;
+
+        /**
+         * Data model tree.
+         */
+        private DataModelTree data;
+
+        /**
+         * Sets workflow id.
+         * @param workflowId workflow id
+         * @return builder
+         */
+        public Builder workflowId(URI workflowId) {
+            this.workflowId = workflowId;
+            return this;
+        }
+
+        /**
+         * Sets workplace name.
+         * @param workplaceName workplace name
+         * @return builder
+         */
+        public Builder workplaceName(String workplaceName) {
+            this.workplaceName = workplaceName;
+            return this;
+        }
+
+        /**
+         * Sets data model tree.
+         * @param data data model tree
+         * @return builder
+         */
+        public Builder data(DataModelTree data) {
+            this.data = data;
+            return this;
+        }
+
+        /**
+         * Builds default workflow context.
+         * @return instance of default workflow context
+         * @throws WorkflowException workflow exception
+         */
+        public DefaultWorkflowContext build() throws WorkflowException {
+            check(data != null, "Invalid data model tree");
+            check(workflowId != null, "Invalid workflowId");
+            check(workplaceName != null, "Invalid workplaceName");
+            return new DefaultWorkflowContext(this);
+        }
+    }
 }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkflowDescription.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkflowDescription.java
index c9ded60..13a8f46 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkflowDescription.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkflowDescription.java
@@ -24,6 +24,8 @@
 
 import java.net.URI;
 
+import static org.onosproject.workflow.api.CheckCondition.check;
+
 
 /**
  * Class for default workflow description.
@@ -201,8 +203,12 @@
         /**
          * Builds workflow description from builder.
          * @return instance of workflow description
+         * @throws WorkflowException workflow exception
          */
-        public DefaultWorkflowDescription build() {
+        public DefaultWorkflowDescription build() throws WorkflowException {
+            check(workplaceName != null, "workplaceName is invalid");
+            check(id != null, "id is invalid");
+            check(data != null, "data is invalid");
             return new DefaultWorkflowDescription(this);
         }
     }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkplaceDescription.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkplaceDescription.java
index 95da550..e8125f7 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkplaceDescription.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkplaceDescription.java
@@ -27,6 +27,8 @@
 import java.util.List;
 import java.util.Optional;
 
+import static org.onosproject.workflow.api.CheckCondition.check;
+
 /**
  * Class for default workplace description.
  */
@@ -161,8 +163,10 @@
         /**
          * Builds workplace description from builder.
          * @return instance of workflow description
+         * @throws WorkflowException workflow exception
          */
-        public DefaultWorkplaceDescription build() {
+        public DefaultWorkplaceDescription build() throws WorkflowException {
+            check(name != null, "name is invalid");
             return new DefaultWorkplaceDescription(this);
         }
     }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/EventHintSupplier.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/EventHintSupplier.java
index 3ad1847..076bfc0 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/EventHintSupplier.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/EventHintSupplier.java
@@ -22,5 +22,5 @@
  */
 @FunctionalInterface
 public interface EventHintSupplier {
-    String apply(Event event);
+    String apply(Event event) throws Throwable;
 }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/EventTask.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/EventTask.java
index 3b2b30a..c2af3e6 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/EventTask.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/EventTask.java
@@ -20,6 +20,8 @@
 
 import java.util.Objects;
 
+import static org.onosproject.workflow.api.CheckCondition.check;
+
 /**
  * Class for event task.
  */
@@ -91,7 +93,7 @@
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
                 .add("context", context())
-                .add("workletType", workletType())
+                .add("programCounter", programCounter())
                 .add("event", event())
                 .add("eventHint", eventHint())
                 .toString();
@@ -147,16 +149,19 @@
         }
 
         @Override
-        public Builder workletType(String workletType) {
-            super.workletType(workletType);
+        public Builder programCounter(ProgramCounter programCounter) {
+            super.programCounter(programCounter);
             return this;
         }
 
         /**
          * Builds EventTask.
          * @return instance of EventTask
+         * @throws WorkflowException workflow exception
          */
-        public EventTask build() {
+        public EventTask build() throws WorkflowException {
+            check(event != null, "event is invalid");
+            check(eventHint != null, "eventHint is invalid");
             return new EventTask(this);
         }
     }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/EventTimeoutTask.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/EventTimeoutTask.java
index 92516d4..9aa870d 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/EventTimeoutTask.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/EventTimeoutTask.java
@@ -19,6 +19,8 @@
 
 import java.util.Objects;
 
+import static org.onosproject.workflow.api.CheckCondition.check;
+
 /**
  * Class for event timeout task.
  */
@@ -81,7 +83,7 @@
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
                 .add("context", context())
-                .add("workletType", workletType())
+                .add("programCounter", programCounter())
                 .add("eventType", eventType())
                 .add("eventHint", eventHint())
                 .toString();
@@ -136,16 +138,19 @@
         }
 
         @Override
-        public Builder workletType(String workletType) {
-            super.workletType(workletType);
+        public Builder programCounter(ProgramCounter programCounter) {
+            super.programCounter(programCounter);
             return this;
         }
 
         /**
          * Builds EventTimeoutTask.
          * @return instance of EventTimeoutTask
+         * @throws WorkflowException workflow exception
          */
-        public EventTimeoutTask build() {
+        public EventTimeoutTask build() throws WorkflowException {
+            check(eventType != null, "eventType is invalid");
+            check(eventHint != null, "eventType is invalid");
             return new EventTimeoutTask(this);
         }
     }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/HandlerTask.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/HandlerTask.java
index 106466b..94203bc 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/HandlerTask.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/HandlerTask.java
@@ -28,9 +28,9 @@
     private final WorkflowContext context;
 
     /**
-     * Worklet type of handler task.
+     * Program counter of handler task.
      */
-    private final String workletType;
+    private final ProgramCounter programCounter;
 
     /**
      * Constructor for handler task.
@@ -38,7 +38,7 @@
      */
     protected HandlerTask(Builder builder) {
         this.context = builder.context;
-        this.workletType = builder.workletType;
+        this.programCounter = builder.programCounter;
     }
 
     /**
@@ -50,18 +50,18 @@
     }
 
     /**
-     * Returns worklet type name of this handler task.
-     * @return worklet type
+     * Returns program counter of this handler task.
+     * @return program counter
      */
-    public String workletType() {
-        return workletType;
+    public ProgramCounter programCounter() {
+        return programCounter;
     }
 
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
                 .add("context", context())
-                .add("workletType", workletType())
+                .add("programCounter", programCounter())
                 .toString();
     }
 
@@ -70,7 +70,7 @@
      */
     public static class Builder {
         protected WorkflowContext context;
-        protected String workletType;
+        protected ProgramCounter programCounter;
 
         /**
          * Sets workflow context of handler task.
@@ -83,12 +83,12 @@
         }
 
         /**
-         * Sets worklet type of handler task.
-         * @param workletType worklet type
+         * Sets program counter of handler task.
+         * @param programCounter program counter of handler type
          * @return builder of handler task
          */
-        public Builder workletType(String workletType) {
-            this.workletType = workletType;
+        public Builder programCounter(ProgramCounter programCounter) {
+            this.programCounter = programCounter;
             return this;
         }
     }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ImmutableListWorkflow.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ImmutableListWorkflow.java
index b5ea456..e948a7a 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ImmutableListWorkflow.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ImmutableListWorkflow.java
@@ -29,6 +29,8 @@
 import java.util.Objects;
 import java.util.Set;
 
+import static org.onosproject.workflow.api.CheckCondition.check;
+
 /**
  * Class for immutable list workflow.
  */
@@ -49,6 +51,8 @@
      */
     private Set<WorkflowAttribute> attributes;
 
+    private static JsonDataModelInjector dataModelInjector = new JsonDataModelInjector();
+
     /**
      * Constructor of ImmutableListWorkflow.
      * @param builder builder of ImmutableListWorkflow
@@ -71,10 +75,14 @@
 
 
     @Override
-    public Worklet next(WorkflowContext context) throws WorkflowException {
+    public ProgramCounter next(WorkflowContext context) throws WorkflowException {
 
         int cnt = 0;
-        for (int i = 0; i < workletTypeList.size(); i++) {
+
+        ProgramCounter pc = context.current();
+        check(pc != null, "Invalid program counter");
+
+        for (int i = pc.workletIndex(); i < workletTypeList.size(); i++) {
 
             if (cnt++ > Worklet.MAX_WORKS) {
                 throw new WorkflowException("Maximum worklet execution exceeded");
@@ -83,7 +91,7 @@
             String workletType = workletTypeList.get(i);
 
             if (Worklet.Common.COMPLETED.tag().equals(workletType)) {
-                return Worklet.Common.COMPLETED;
+                return ProgramCounter.valueOf(workletType, i);
             }
 
             if (Worklet.Common.INIT.tag().equals(workletType)) {
@@ -109,17 +117,35 @@
                 continue;
 
             } else {
+                // isNext is read only. It does not perform 'inhale'.
+                dataModelInjector.inject(worklet, context);
                 if (worklet.isNext(context)) {
-                    return worklet;
+                    return ProgramCounter.valueOf(workletType, i);
                 }
             }
         }
-        return Worklet.Common.COMPLETED;
+        throw new WorkflowException("workflow reached to end but not COMPLETED");
+    }
+
+    @Override
+    public ProgramCounter increased(ProgramCounter pc) throws WorkflowException {
+
+        int increaedIndex = pc.workletIndex() + 1;
+        if (increaedIndex >= workletTypeList.size()) {
+            throw new WorkflowException("Out of bound in program counter(" + pc + ")");
+        }
+
+        String workletType = workletTypeList.get(increaedIndex);
+        return ProgramCounter.valueOf(workletType, increaedIndex);
     }
 
     @Override
     public Worklet getWorkletInstance(String workletType) throws WorkflowException {
 
+        if (Worklet.Common.COMPLETED.tag().equals(workletType)) {
+            return Worklet.Common.COMPLETED;
+        }
+
         WorkflowStore store;
         try {
              store = DefaultServiceDirectory.getService(WorkflowStore.class);
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModel.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModel.java
new file mode 100644
index 0000000..1dc8e52
--- /dev/null
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModel.java
@@ -0,0 +1,50 @@
+/*
+ * 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.fasterxml.jackson.databind.JsonNode;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+
+/**
+ * Annotation for injecting json data model on work-let execution context.
+ */
+public @interface JsonDataModel {
+
+    /**
+     * Path of data model.
+     * @return path of data model
+     */
+    String path() default "/";
+
+    /**
+     * Type of data model.
+     * @return type of data model
+     */
+    Class<?> type() default JsonNode.class;
+
+    /**
+     * Representing whether this data model is optional or not.
+     * @return optional or not
+     */
+    boolean optional() default false;
+}
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelInjector.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelInjector.java
new file mode 100644
index 0000000..d76aa52
--- /dev/null
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelInjector.java
@@ -0,0 +1,589 @@
+/*
+ * 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.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.BooleanNode;
+import com.fasterxml.jackson.databind.node.IntNode;
+import com.fasterxml.jackson.databind.node.MissingNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Class for injecting json data model on the work-let execution context.
+ */
+public class JsonDataModelInjector {
+
+    private static final Logger log = LoggerFactory.getLogger(JsonDataModelInjector.class);
+
+    /**
+     * Injects data model to work-let.
+     * @param worklet work-let to be injected
+     * @param context workflow context
+     * @throws WorkflowException workflow exception
+     */
+    public void inject(Worklet worklet, WorkflowContext context) throws WorkflowException {
+
+        handle(worklet, context, this::injectModel);
+    }
+
+    /**
+     * Inhales data model from work-let.
+     * @param worklet work-let to be inhaled
+     * @param context workflow context
+     * @throws WorkflowException workflow exception
+     */
+    public void inhale(Worklet worklet, WorkflowContext context) throws WorkflowException {
+
+        handle(worklet, context, this::inhaleModel);
+    }
+
+    private void handle(Worklet worklet, WorkflowContext context, DataModelFieldBehavior func)
+            throws WorkflowException {
+        Class cl = worklet.getClass();
+        List<Field> fields = getInheritedFields(cl);
+        if (Objects.isNull(fields)) {
+            log.error("Invalid fields on {}", cl);
+            return;
+        }
+
+        for (Field field: fields) {
+            Annotation[] annotations = field.getAnnotations();
+            if (Objects.isNull(annotations)) {
+                continue;
+            }
+            for (Annotation annotation: annotations) {
+                if (!(annotation instanceof JsonDataModel)) {
+                    continue;
+                }
+                JsonDataModel model = (JsonDataModel) annotation;
+                func.apply(worklet, context, field, model);
+            }
+        }
+    }
+
+    private static List<Field> getInheritedFields(Class<?> type) {
+        List<Field> fields = new ArrayList<Field>();
+
+        Class<?> cl = type;
+        while (cl != null && cl != Object.class) {
+            for (Field field : cl.getDeclaredFields()) {
+                if (!field.isSynthetic()) {
+                    fields.add(field);
+                }
+            }
+            cl = cl.getSuperclass();
+        }
+        return fields;
+    }
+
+    /**
+     * Functional interface for json data model annotated field behavior.
+     */
+    @FunctionalInterface
+    public interface DataModelFieldBehavior {
+        void apply(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+                throws WorkflowException;
+    }
+
+    private static Map<Class, DataModelFieldBehavior> injectTypeMap = new HashMap<>();
+    static {
+        injectTypeMap.put(String.class, JsonDataModelInjector::injectText);
+        injectTypeMap.put(Integer.class, JsonDataModelInjector::injectInteger);
+        injectTypeMap.put(Boolean.class, JsonDataModelInjector::injectBoolean);
+        injectTypeMap.put(JsonNode.class, JsonDataModelInjector::injectJsonNode);
+        injectTypeMap.put(ArrayNode.class, JsonDataModelInjector::injectArrayNode);
+        injectTypeMap.put(ObjectNode.class, JsonDataModelInjector::injectObjectNode);
+    }
+
+    /**
+     * Injects data model on the filed of work-let.
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field the field of work-let
+     * @param model data model for the field
+     * @throws WorkflowException workflow exception
+     */
+    private void injectModel(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+            throws WorkflowException {
+
+        DataModelFieldBehavior behavior = injectTypeMap.get(model.type());
+        if (Objects.isNull(behavior)) {
+            throw new WorkflowException("Not supported type(" + model.type() + ")");
+        }
+        behavior.apply(worklet, context, field, model);
+    }
+
+    /**
+     * Injects text data model on the filed of work-let.
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field the field of work-let
+     * @param model text data model for the field
+     * @throws WorkflowException workflow exception
+     */
+    private static void injectText(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+            throws WorkflowException {
+
+        String text = ((JsonDataModelTree) context.data()).textAt(model.path());
+        if (Objects.isNull(text)) {
+            if (model.optional()) {
+                return;
+            }
+            throw new WorkflowException("Invalid text data model on (" + model.path() + ")");
+        }
+
+        if (!(Objects.equals(field.getType(), String.class))) {
+            throw new WorkflowException("Target field (" + field + ") is not String");
+        }
+
+        try {
+            field.setAccessible(true);
+            field.set(worklet, text);
+        } catch (IllegalAccessException e) {
+            throw new WorkflowException(e);
+        }
+    }
+
+    /**
+     * Injects integer data model on the filed of work-let.
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field the field of work-let
+     * @param model integer data model for the field
+     * @throws WorkflowException workflow exception
+     */
+    private static void injectInteger(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+            throws WorkflowException {
+
+        Integer number = ((JsonDataModelTree) context.data()).intAt(model.path());
+        if (Objects.isNull(number)) {
+            if (model.optional()) {
+                return;
+            }
+            throw new WorkflowException("Invalid number data model on (" + model.path() + ")");
+        }
+
+        if (!(Objects.equals(field.getType(), Integer.class))) {
+            throw new WorkflowException("Target field (" + field + ") is not Integer");
+        }
+
+        try {
+            field.setAccessible(true);
+            field.set(worklet, number);
+        } catch (IllegalAccessException e) {
+            throw new WorkflowException(e);
+        }
+    }
+
+    /**
+     * Injects boolean data model on the filed of work-let.
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field the field of work-let
+     * @param model boolean data model for the field
+     * @throws WorkflowException workflow exception
+     */
+    private static void injectBoolean(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+            throws WorkflowException {
+
+        Boolean bool = ((JsonDataModelTree) context.data()).booleanAt(model.path());
+        if (Objects.isNull(bool)) {
+            if (model.optional()) {
+                return;
+            }
+            throw new WorkflowException("Invalid boolean data model on (" + model.path() + ")");
+        }
+
+        if (!(Objects.equals(field.getType(), Boolean.class))) {
+            throw new WorkflowException("Target field (" + field + ") is not Boolean");
+        }
+
+        try {
+            field.setAccessible(true);
+            field.set(worklet, bool);
+        } catch (IllegalAccessException e) {
+            throw new WorkflowException(e);
+        }
+    }
+
+    /**
+     * Injects json node data model on the filed of work-let.
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field the field of work-let
+     * @param model json node data model for the field
+     * @throws WorkflowException workflow exception
+     */
+    private static void injectJsonNode(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+            throws WorkflowException {
+
+        JsonNode jsonNode = ((JsonDataModelTree) context.data()).nodeAt(model.path());
+        if (Objects.isNull(jsonNode)) {
+            if (model.optional()) {
+                return;
+            }
+            throw new WorkflowException("Invalid json node data model on (" + model.path() + ")");
+        }
+
+        if (!(Objects.equals(field.getType(), JsonNode.class))) {
+            throw new WorkflowException("Target field (" + field + ") is not JsonNode");
+        }
+
+        try {
+            field.setAccessible(true);
+            field.set(worklet, jsonNode);
+        } catch (IllegalAccessException e) {
+            throw new WorkflowException(e);
+        }
+    }
+
+    /**
+     * Injects json array node data model on the filed of work-let.
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field the field of work-let
+     * @param model json array node data model for the field
+     * @throws WorkflowException workflow exception
+     */
+    private static void injectArrayNode(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+            throws WorkflowException {
+
+        ArrayNode arrayNode = ((JsonDataModelTree) context.data()).arrayAt(model.path());
+        if (Objects.isNull(arrayNode)) {
+            if (model.optional()) {
+                return;
+            }
+            throw new WorkflowException("Invalid array node data model on (" + model.path() + ")");
+        }
+
+        if (!(Objects.equals(field.getType(), ArrayNode.class))) {
+            throw new WorkflowException("Target field (" + field + ") is not ArrayNode");
+        }
+
+        try {
+            field.setAccessible(true);
+            field.set(worklet, arrayNode);
+        } catch (IllegalAccessException e) {
+            throw new WorkflowException(e);
+        }
+    }
+
+    /**
+     * Injects json object node data model on the filed of work-let.
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field the field of work-let
+     * @param model json object node data model for the field
+     * @throws WorkflowException workflow exception
+     */
+    private static void injectObjectNode(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+            throws WorkflowException {
+
+        ObjectNode objNode = ((JsonDataModelTree) context.data()).objectAt(model.path());
+        if (Objects.isNull(objNode)) {
+            if (model.optional()) {
+                return;
+            }
+            throw new WorkflowException("Invalid object node data model on (" + model.path() + ")");
+        }
+
+        if (!(Objects.equals(field.getType(), ObjectNode.class))) {
+            throw new WorkflowException("Target field (" + field + ") is not ObjectNode");
+        }
+
+        try {
+            field.setAccessible(true);
+            field.set(worklet, objNode);
+        } catch (IllegalAccessException e) {
+            throw new WorkflowException(e);
+        }
+    }
+
+    private static Map<Class, DataModelFieldBehavior> inhaleTypeMap = new HashMap<>();
+    static {
+        inhaleTypeMap.put(String.class, JsonDataModelInjector::inhaleText);
+        inhaleTypeMap.put(Integer.class, JsonDataModelInjector::inhaleInteger);
+        inhaleTypeMap.put(Boolean.class, JsonDataModelInjector::inhaleBoolean);
+        inhaleTypeMap.put(JsonNode.class, JsonDataModelInjector::inhaleJsonNode);
+        inhaleTypeMap.put(ArrayNode.class, JsonDataModelInjector::inhaleArrayNode);
+        inhaleTypeMap.put(ObjectNode.class, JsonDataModelInjector::inhaleObjectNode);
+    }
+
+    /**
+     * Inhales data model on the filed of work-let.
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field the field of work-let
+     * @param model data model for the field
+     * @throws WorkflowException workflow exception
+     */
+    private void inhaleModel(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+            throws WorkflowException {
+
+        DataModelFieldBehavior behavior = inhaleTypeMap.get(model.type());
+        if (Objects.isNull(behavior)) {
+            throw new WorkflowException("Not supported type(" + model.type() + ")");
+        }
+        behavior.apply(worklet, context, field, model);
+    }
+
+    /**
+     * Inhales text data model on the filed of work-let.
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field the field of work-let
+     * @param model text data model for the field
+     * @throws WorkflowException workflow exception
+     */
+    private static void inhaleText(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+            throws WorkflowException {
+
+        if (!(Objects.equals(field.getType(), String.class))) {
+            throw new WorkflowException("Target field (" + field + ") is not String");
+        }
+
+        String text;
+        try {
+            field.setAccessible(true);
+            text = (String) field.get(worklet);
+        } catch (IllegalAccessException e) {
+            throw new WorkflowException(e);
+        }
+
+        if (Objects.isNull(text)) {
+            return;
+        }
+
+        JsonDataModelTree tree = (JsonDataModelTree) context.data();
+        JsonNode jsonNode = tree.nodeAt(model.path());
+
+        if (Objects.isNull(jsonNode) || jsonNode instanceof MissingNode) {
+            tree.setAt(model.path(), text);
+        } else if (!(jsonNode instanceof TextNode)) {
+            throw new WorkflowException("Invalid text data model on (" + model.path() + ")");
+        } else {
+            tree.remove(model.path());
+            tree.setAt(model.path(), text);
+        }
+    }
+
+    /**
+     * Inhales integer data model on the filed of work-let.
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field the field of work-let
+     * @param model integer data model for the field
+     * @throws WorkflowException workflow exception
+     */
+    private static void inhaleInteger(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+            throws WorkflowException {
+
+        if (!(Objects.equals(field.getType(), Integer.class))) {
+            throw new WorkflowException("Target field (" + field + ") is not Integer");
+        }
+
+        Integer number;
+        try {
+            field.setAccessible(true);
+            number = (Integer) field.get(worklet);
+        } catch (IllegalAccessException e) {
+            throw new WorkflowException(e);
+        }
+
+        if (Objects.isNull(number)) {
+            return;
+        }
+
+        JsonDataModelTree tree = (JsonDataModelTree) context.data();
+        JsonNode jsonNode = tree.nodeAt(model.path());
+
+        if (Objects.isNull(jsonNode) || jsonNode instanceof MissingNode) {
+            tree.setAt(model.path(), number);
+        } else if (!(jsonNode instanceof IntNode)) {
+            throw new WorkflowException("Invalid integer data model on (" + model.path() + ")");
+        } else {
+            tree.remove(model.path());
+            tree.setAt(model.path(), number);
+        }
+    }
+
+    /**
+     * Inhales boolean data model on the filed of work-let.
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field the field of work-let
+     * @param model boolean data model for the field
+     * @throws WorkflowException workflow exception
+     */
+    private static void inhaleBoolean(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+            throws WorkflowException {
+
+        if (!(Objects.equals(field.getType(), Boolean.class))) {
+            throw new WorkflowException("Target field (" + field + ") is not Boolean");
+        }
+
+        Boolean bool;
+        try {
+            field.setAccessible(true);
+            bool = (Boolean) field.get(worklet);
+        } catch (IllegalAccessException e) {
+            throw new WorkflowException(e);
+        }
+
+        if (Objects.isNull(bool)) {
+            return;
+        }
+
+        JsonDataModelTree tree = (JsonDataModelTree) context.data();
+        JsonNode jsonNode = tree.nodeAt(model.path());
+
+        if (Objects.isNull(jsonNode) || jsonNode instanceof MissingNode) {
+            tree.setAt(model.path(), bool);
+        } else if (!(jsonNode instanceof BooleanNode)) {
+            throw new WorkflowException("Invalid boolean data model on (" + model.path() + ")");
+        } else {
+            tree.remove(model.path());
+            tree.setAt(model.path(), bool);
+        }
+    }
+
+    /**
+     * Inhales json node data model on the filed of work-let.
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field the field of work-let
+     * @param model json node data model for the field
+     * @throws WorkflowException workflow exception
+     */
+    private static void inhaleJsonNode(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+            throws WorkflowException {
+
+        if (!(Objects.equals(field.getType(), JsonNode.class))) {
+            throw new WorkflowException("Target field (" + field + ") is not JsonNode");
+        }
+
+        JsonNode tgtJsonNode;
+        try {
+            field.setAccessible(true);
+            tgtJsonNode = (JsonNode) field.get(worklet);
+        } catch (IllegalAccessException e) {
+            throw new WorkflowException(e);
+        }
+
+        if (Objects.isNull(tgtJsonNode)) {
+            return;
+        }
+
+        JsonDataModelTree tree = (JsonDataModelTree) context.data();
+        JsonNode jsonNode = tree.nodeAt(model.path());
+
+        if (Objects.isNull(jsonNode) || jsonNode instanceof MissingNode) {
+            tree.attach(model.path(), new JsonDataModelTree(tgtJsonNode));
+        } else if (!(jsonNode instanceof JsonNode)) {
+            throw new WorkflowException("Invalid json node data model on (" + model.path() + ")");
+        } else {
+            // do nothing
+        }
+    }
+
+    /**
+     * Inhales json array node data model on the filed of work-let.
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field the field of work-let
+     * @param model json array node data model for the field
+     * @throws WorkflowException workflow exception
+     */
+    private static void inhaleArrayNode(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+            throws WorkflowException {
+        if (!(Objects.equals(field.getType(), ArrayNode.class))) {
+            throw new WorkflowException("Target field (" + field + ") is not ArrayNode");
+        }
+
+        ArrayNode tgtArrayNode;
+        try {
+            field.setAccessible(true);
+            tgtArrayNode = (ArrayNode) field.get(worklet);
+        } catch (IllegalAccessException e) {
+            throw new WorkflowException(e);
+        }
+
+        if (Objects.isNull(tgtArrayNode)) {
+            return;
+        }
+
+        JsonDataModelTree tree = (JsonDataModelTree) context.data();
+        JsonNode jsonNode = tree.nodeAt(model.path());
+
+        if (Objects.isNull(jsonNode) || jsonNode instanceof MissingNode) {
+            tree.attach(model.path(), new JsonDataModelTree(tgtArrayNode));
+        } else if (!(jsonNode instanceof ArrayNode)) {
+            throw new WorkflowException("Invalid array node data model on (" + model.path() + ")");
+        } else {
+            // do nothing
+        }
+    }
+
+    /**
+     * Inhales json object node data model on the filed of work-let.
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field the field of work-let
+     * @param model json object node data model for the field
+     * @throws WorkflowException workflow exception
+     */
+    private static void inhaleObjectNode(Worklet worklet, WorkflowContext context, Field field, JsonDataModel model)
+            throws WorkflowException {
+        if (!(Objects.equals(field.getType(), ObjectNode.class))) {
+            throw new WorkflowException("Target field (" + field + ") is not ObjectNode");
+        }
+
+        ObjectNode tgtObjNode;
+        try {
+            field.setAccessible(true);
+            tgtObjNode = (ObjectNode) field.get(worklet);
+        } catch (IllegalAccessException e) {
+            throw new WorkflowException(e);
+        }
+
+        if (Objects.isNull(tgtObjNode)) {
+            return;
+        }
+
+        JsonDataModelTree tree = (JsonDataModelTree) context.data();
+        JsonNode jsonNode = tree.nodeAt(model.path());
+
+        if (Objects.isNull(jsonNode) || jsonNode instanceof MissingNode) {
+            tree.attach(model.path(), new JsonDataModelTree(tgtObjNode));
+        } else if (!(jsonNode instanceof ObjectNode)) {
+            throw new WorkflowException("Invalid object node data model on (" + model.path() + ")");
+        } else {
+            // do nothing
+        }
+    }
+}
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelTree.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelTree.java
index 3a74bd2..7ee1be7 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelTree.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelTree.java
@@ -21,11 +21,17 @@
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.BooleanNode;
+import com.fasterxml.jackson.databind.node.IntNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.MissingNode;
+import com.fasterxml.jackson.databind.node.NumericNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
 import com.google.common.base.MoreObjects;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.Objects;
 
@@ -34,6 +40,8 @@
  */
 public final class JsonDataModelTree implements DataModelTree {
 
+    private static final Logger log = LoggerFactory.getLogger(JsonDataModelTree.class);
+
     /**
      * Root node of json data model tree.
      */
@@ -65,30 +73,88 @@
 
     @Override
     public void attach(String path, DataModelTree tree) throws WorkflowException {
+
         if (root == null || root instanceof MissingNode) {
             throw new WorkflowException("Invalid root node");
         }
 
         JsonPointer ptr = JsonPointer.compile(path);
-        JsonNode node = root.at(ptr);
-        if (!(node instanceof MissingNode)) {
-            throw new WorkflowException("Path(" + path + ") has already subtree(" + node + ")");
-        }
 
         if (!(tree instanceof JsonDataModelTree)) {
             throw new WorkflowException("Invalid subTree(" + tree + ")");
         }
         JsonNode attachingNode = ((JsonDataModelTree) tree).root();
 
-        alloc(ptr.head(), Nodetype.MAP);
-        JsonNode parentNode = root.at(ptr.head());
+        attach(ptr, attachingNode);
+    }
 
-        if (!parentNode.isObject()) {
-            throw new WorkflowException("Invalid parentNode type(" + parentNode.getNodeType() + ")");
+    private void attach(JsonPointer ptr, JsonNode attachingNode) throws WorkflowException {
+
+        JsonNode node = root.at(ptr);
+        if (!(node instanceof MissingNode)) {
+            throw new WorkflowException("Path(" + ptr + ") has already subtree(" + node + ")");
         }
 
-        String key = ptr.last().getMatchingProperty();
-        ((ObjectNode) parentNode).put(key, attachingNode);
+        if (ptr.last().getMatchingIndex() != -1) {
+
+            alloc(ptr.head(), Nodetype.ARRAY);
+            JsonNode parentNode = root.at(ptr.head());
+            if (!parentNode.isArray()) {
+                throw new WorkflowException("Invalid parentNode type(" + parentNode.getNodeType() + " != Array)");
+            }
+            int index = ptr.last().getMatchingIndex();
+            ((ArrayNode) parentNode).insert(index, attachingNode);
+
+        } else if (ptr.last().getMatchingProperty() != null) {
+
+            alloc(ptr.head(), Nodetype.MAP);
+            JsonNode parentNode = root.at(ptr.head());
+            if (!parentNode.isObject()) {
+                throw new WorkflowException("Invalid parentNode type(" + parentNode.getNodeType() + " != Object)");
+            }
+            String key = ptr.last().getMatchingProperty();
+            ((ObjectNode) parentNode).put(key, attachingNode);
+
+        } else {
+            throw new WorkflowException("Invalid path(" + ptr + ")");
+        }
+    }
+
+    @Override
+    public void remove(String path) throws WorkflowException {
+        JsonPointer ptr = JsonPointer.compile(path);
+        remove(ptr);
+    }
+
+    private void remove(JsonPointer ptr) throws WorkflowException {
+
+        JsonNode node = root.at(ptr);
+        if (node instanceof MissingNode) {
+            log.warn("{} does not have valid node", ptr);
+            return;
+        }
+
+        if (ptr.last().getMatchingIndex() != -1) {
+
+            JsonNode parentNode = root.at(ptr.head());
+            if (!parentNode.isArray()) {
+                throw new WorkflowException("Invalid parentNode type(" + parentNode.getNodeType() + " != Array)");
+            }
+            int index = ptr.last().getMatchingIndex();
+            ((ArrayNode) parentNode).remove(index);
+
+        } else if (ptr.last().getMatchingProperty() != null) {
+
+            JsonNode parentNode = root.at(ptr.head());
+            if (!parentNode.isObject()) {
+                throw new WorkflowException("Invalid parentNode type(" + parentNode.getNodeType() + " != Object)");
+            }
+            String key = ptr.last().getMatchingProperty();
+            ((ObjectNode) parentNode).remove(key);
+
+        } else {
+            throw new WorkflowException("Invalid path(" + ptr + ")");
+        }
     }
 
     @Override
@@ -200,6 +266,9 @@
             throw new WorkflowException("Invalid root node");
         }
         JsonNode node = root.at(ptr);
+        if (node instanceof MissingNode) {
+            return null;
+        }
         if (!(node instanceof ObjectNode)) {
             throw new WorkflowException("Invalid node(" + node + ") at " + ptr);
         }
@@ -228,6 +297,9 @@
             throw new WorkflowException("Invalid root node");
         }
         JsonNode node = root.at(ptr);
+        if (node instanceof MissingNode) {
+            return null;
+        }
         if (!(node instanceof ArrayNode)) {
             throw new WorkflowException("Invalid node(" + node + ") at " + ptr);
         }
@@ -235,6 +307,165 @@
     }
 
     /**
+     * Gets text node on specific path.
+     * @param path path of json node
+     * @return text on specific path
+     * @throws WorkflowException workflow exception
+     */
+    public String textAt(String path) throws WorkflowException {
+        JsonPointer ptr = JsonPointer.compile(path);
+        return textAt(ptr);
+    }
+
+    /**
+     * Gets text on specific json pointer.
+     * @param ptr json pointer
+     * @return text on specific json pointer
+     * @throws WorkflowException workflow exception
+     */
+    public String textAt(JsonPointer ptr) throws WorkflowException {
+        if (root == null || root instanceof MissingNode) {
+            throw new WorkflowException("Invalid root node");
+        }
+        JsonNode node = root.at(ptr);
+        if (node instanceof MissingNode) {
+            return null;
+        }
+        if (!(node instanceof TextNode)) {
+            throw new WorkflowException("Invalid node(" + node + ") at " + ptr);
+        }
+        return ((TextNode) node).asText();
+    }
+
+    /**
+     * Gets integer node on specific path.
+     * @param path path of json node
+     * @return integer on specific path
+     * @throws WorkflowException workflow exception
+     */
+    public Integer intAt(String path) throws WorkflowException {
+        JsonPointer ptr = JsonPointer.compile(path);
+        return intAt(ptr);
+    }
+
+    /**
+     * Gets integer on specific json pointer.
+     * @param ptr json pointer
+     * @return integer on specific json pointer
+     * @throws WorkflowException workflow exception
+     */
+    public Integer intAt(JsonPointer ptr) throws WorkflowException {
+        if (root == null || root instanceof MissingNode) {
+            throw new WorkflowException("Invalid root node");
+        }
+        JsonNode node = root.at(ptr);
+        if (node instanceof MissingNode) {
+            return null;
+        }
+        if (!(node instanceof NumericNode)) {
+            throw new WorkflowException("Invalid node(" + node + ") at " + ptr);
+        }
+        return ((NumericNode) node).asInt();
+    }
+
+    /**
+     * Gets boolean on specific path.
+     * @param path path of json node
+     * @return boolean on specific path
+     * @throws WorkflowException workflow exception
+     */
+    public Boolean booleanAt(String path) throws WorkflowException {
+        JsonPointer ptr = JsonPointer.compile(path);
+        return booleanAt(ptr);
+    }
+
+    /**
+     * Gets boolean on specific json pointer.
+     * @param ptr json pointer
+     * @return boolean on specific json pointer
+     * @throws WorkflowException workflow exception
+     */
+    public Boolean booleanAt(JsonPointer ptr) throws WorkflowException {
+        if (root == null || root instanceof MissingNode) {
+            throw new WorkflowException("Invalid root node");
+        }
+        JsonNode node = root.at(ptr);
+        if (node instanceof MissingNode) {
+            return null;
+        }
+        if (!(node instanceof BooleanNode)) {
+            throw new WorkflowException("Invalid node(" + node + ") at " + ptr);
+        }
+        return ((BooleanNode) node).asBoolean();
+    }
+
+    /**
+     * Sets text on specific json path.
+     * @param path json path
+     * @param text text to set
+     * @throws WorkflowException workflow exception
+     */
+    public void setAt(String path, String text) throws WorkflowException {
+        JsonPointer ptr = JsonPointer.compile(path);
+        setAt(ptr, text);
+    }
+
+    /**
+     * Sets text on the specific json pointer.
+     * @param ptr json pointer
+     * @param text text to set
+     * @throws WorkflowException workflow exception
+     */
+    public void setAt(JsonPointer ptr, String text) throws WorkflowException {
+        TextNode textNode = TextNode.valueOf(text);
+        attach(ptr, textNode);
+    }
+
+    /**
+     * Sets boolean on specific json path.
+     * @param path json path
+     * @param isTrue boolean to set
+     * @throws WorkflowException workflow exception
+     */
+    public void setAt(String path, Boolean isTrue) throws WorkflowException {
+        JsonPointer ptr = JsonPointer.compile(path);
+        setAt(ptr, isTrue);
+    }
+
+    /**
+     * Sets boolean on the specific json pointer.
+     * @param ptr json pointer
+     * @param isTrue boolean to set
+     * @throws WorkflowException workflow exception
+     */
+    public void setAt(JsonPointer ptr, Boolean isTrue) throws WorkflowException {
+        BooleanNode booleanNode = BooleanNode.valueOf(isTrue);
+        attach(ptr, booleanNode);
+    }
+
+    /**
+     * Sets integer on specific json path.
+     * @param path json path
+     * @param number number to set
+     * @throws WorkflowException workflow exception
+     */
+    public void setAt(String path, Integer number) throws WorkflowException {
+        JsonPointer ptr = JsonPointer.compile(path);
+        setAt(ptr, number);
+    }
+
+    /**
+     * Sets integer on the specific json pointer.
+     * @param ptr json pointer
+     * @param number number to set
+     * @throws WorkflowException workflow exception
+     */
+    public void setAt(JsonPointer ptr, Integer number) throws WorkflowException {
+        IntNode intNode = IntNode.valueOf(number);
+        attach(ptr, intNode);
+    }
+
+    /**
      * Allocates json data model tree on json pointer path with specific leaf type.
      * @param node current json node in the json tree path
      * @param ptr json pointer
@@ -245,11 +476,11 @@
     private JsonNode alloc(JsonNode node, JsonPointer ptr, JsonNodeType leaftype) throws WorkflowException {
 
         if (ptr.matches()) {
-            if (node instanceof MissingNode) {
+            if (node == null || node instanceof MissingNode) {
                 node = createEmpty(leaftype);
             } else {
                 //TODO: checking existing node type is matched with leaftype
-                if (Objects.equals(node.getNodeType(), leaftype)) {
+                if (!Objects.equals(node.getNodeType(), leaftype)) {
                     throw new WorkflowException("Requesting leaftype(" + leaftype + ") is not matched with "
                             + "existing nodetype(" + node.getNodeType() + ") for " + ptr);
                 }
@@ -258,7 +489,7 @@
         }
 
         if (ptr.getMatchingIndex() != -1) {
-            if (node instanceof MissingNode) {
+            if (node == null || node instanceof MissingNode) {
                 node = createEmpty(JsonNodeType.ARRAY);
             }
             JsonNode child = alloc(node.get(ptr.getMatchingIndex()), ptr.tail(), leaftype);
@@ -266,7 +497,7 @@
                 ((ArrayNode) node).insert(ptr.getMatchingIndex(), child);
             }
         } else if (ptr.getMatchingProperty() != null) {
-            if (node instanceof MissingNode) {
+            if (node == null || node instanceof MissingNode) {
                 node = createEmpty(JsonNodeType.OBJECT);
             }
             JsonNode child = alloc(node.get(ptr.getMatchingProperty()), ptr.tail(), leaftype);
@@ -304,7 +535,7 @@
         try {
             str = (new ObjectMapper()).writerWithDefaultPrettyPrinter().writeValueAsString(root);
         } catch (JsonProcessingException e) {
-            e.printStackTrace();
+            log.error("Exception: ", e);
         }
         return str;
     }
@@ -312,7 +543,7 @@
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
-                .add("json", formattedRootString())
+                .add("json", root)
                 .toString();
     }
 }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ProgramCounter.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ProgramCounter.java
new file mode 100644
index 0000000..6af50e0
--- /dev/null
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ProgramCounter.java
@@ -0,0 +1,114 @@
+/*
+ * 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 java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * An interface representing workflow program counter.
+ */
+public final class ProgramCounter {
+
+    public static final ProgramCounter INIT_PC = ProgramCounter.valueOf(Worklet.Common.INIT.name(), 0);
+
+    /**
+     * index of the worklet.
+     */
+    private int workletIndex;
+
+    /**
+     * Type of worklet.
+     */
+    private String workletType;
+
+    /**
+     * Index of worklet.
+     * @return index of worklet
+     */
+    public int workletIndex() {
+        return this.workletIndex;
+    }
+
+    /**
+     * Type of worklet.
+     * @return type of worklet
+     */
+    public String workletType() {
+        return this.workletType;
+    }
+
+    /**
+     * Constructor of workflow Program Counter.
+     * @param workletType type of worklet
+     * @param workletIndex index of worklet
+     */
+    private ProgramCounter(String workletType, int workletIndex) {
+        this.workletType = workletType;
+        this.workletIndex = workletIndex;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(this.toString());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof ProgramCounter)) {
+            return false;
+        }
+        return Objects.equals(this.workletType(), ((ProgramCounter) obj).workletType())
+                && Objects.equals(this.workletIndex(), ((ProgramCounter) obj).workletIndex());
+    }
+
+    @Override
+    public String toString() {
+        return String.format("(%d)%s", workletIndex, workletType);
+    }
+
+    /**
+     * Builder of workflow Program Counter.
+     * @param workletType type of worklet
+     * @param workletIndex index of worklet
+     * @return program counter
+     */
+    public static ProgramCounter valueOf(String workletType, int workletIndex) {
+        return new ProgramCounter(workletType, workletIndex);
+    }
+
+    /**
+     * Builder of workflow Program Counter.
+     * @param strProgramCounter string format for program counter
+     * @return program counter
+     */
+    public static ProgramCounter valueOf(String strProgramCounter) {
+
+        Matcher m = Pattern.compile("\\((\\d+)\\)(.+)").matcher(strProgramCounter);
+
+        if (!m.matches()) {
+            throw new IllegalArgumentException("Malformed program counter string");
+        }
+
+        return new ProgramCounter(m.group(2), Integer.parseInt(m.group(1)));
+    }
+
+}
+
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/SystemWorkflowContext.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/SystemWorkflowContext.java
index 5642c97..4cc15bd 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/SystemWorkflowContext.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/SystemWorkflowContext.java
@@ -17,8 +17,6 @@
 
 import com.google.common.base.MoreObjects;
 
-import java.net.URI;
-
 /**
  * WorkflowContext for system workflow.
  */
@@ -36,12 +34,10 @@
 
     /**
      * The constructor of SystemWorkflowContext.
-     * @param workflowId id of workflow
-     * @param workplaceName workplace name
-     * @param data data model tree
+     * @param builder builder of SystemWorkflowContext
      */
-    public SystemWorkflowContext(URI workflowId, String workplaceName, DataModelTree data) {
-        super(workflowId, workplaceName, data);
+    public SystemWorkflowContext(Builder builder) {
+        super(builder);
         timestamp = System.currentTimeMillis();
         //initial distributor(It can be changed)
         distributor = name();
@@ -78,4 +74,27 @@
                 .add("cause", cause())
                 .toString();
     }
+
+    /**
+     * Gets systemBuilder instance.
+     * @return systemBuilder instance
+     */
+    public static final Builder systemBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for system workflow context.
+     */
+    public static class Builder extends DefaultWorkflowContext.Builder {
+
+        /**
+         * Builds system workflow context.
+         * @return instance of default workflow context.
+         */
+        public SystemWorkflowContext build() {
+            return new SystemWorkflowContext(this);
+        }
+    }
+
 }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/TimeoutTask.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/TimeoutTask.java
index 5b98bd1..180d9eb 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/TimeoutTask.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/TimeoutTask.java
@@ -17,6 +17,8 @@
 
 import com.google.common.base.MoreObjects;
 
+import static org.onosproject.workflow.api.CheckCondition.check;
+
 
 public final class TimeoutTask extends HandlerTask {
 
@@ -28,7 +30,7 @@
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
                 .add("context", context())
-                .add("workletType", workletType())
+                .add("programCounter", programCounter())
                 .toString();
     }
 
@@ -51,16 +53,19 @@
         }
 
         @Override
-        public Builder workletType(String workletType) {
-            super.workletType(workletType);
+        public Builder programCounter(ProgramCounter programCounter) {
+            super.programCounter(programCounter);
             return this;
         }
 
         /**
          * Builds TimeoutTask.
          * @return instance of TimeoutTask
+         * @throws WorkflowException workflow exception
          */
-        public TimeoutTask build() {
+        public TimeoutTask build() throws WorkflowException {
+            check(context != null, "context is invalid");
+            check(programCounter != null, "programCounter is invalid");
             return new TimeoutTask(this);
         }
     }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/TimerChain.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/TimerChain.java
index 1118568..afa2ac6 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/TimerChain.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/TimerChain.java
@@ -32,6 +32,13 @@
     private TimerChainTask impendingTask;
 
     /**
+     * Constructor of timer chain.
+     */
+    public TimerChain() {
+
+    }
+
+    /**
      * Schedules timer event.
      * @param afterMs millisecond which time event happens.
      * @param runnable runnable to be executed after 'afterMs'
@@ -55,7 +62,7 @@
      */
     private void schedule(TimerChainTask task) {
         synchronized (this) {
-            if (taskQueue.size() == 0) {
+            if (taskQueue.isEmpty()) {
                 scheduleImpending(task);
                 return;
             }
@@ -124,7 +131,7 @@
      * @return timer chain task
      */
     public TimerChainTask head() {
-        if (taskQueue.size() > 0) {
+        if (!taskQueue.isEmpty()) {
             return taskQueue.peek();
         } else {
             return null;
@@ -136,7 +143,7 @@
      * @return timer chain task
      */
     public TimerChainTask pop() {
-        if (taskQueue.size() > 0) {
+        if (!taskQueue.isEmpty()) {
             return taskQueue.poll();
         } else {
             return null;
@@ -186,7 +193,6 @@
             TimerChainTask nextTask;
             synchronized (timerchain) {
                 if (timerchain.impendingTask() != this) {
-                    System.out.println("Invalid impendingTask");
                     runnable().run();
                     return;
                 }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/Workflow.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/Workflow.java
index 95e6e08..96e623f 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/Workflow.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/Workflow.java
@@ -38,12 +38,20 @@
     Worklet init(WorkflowContext context) throws WorkflowException;
 
     /**
-     * Returns next worklet.
+     * Returns next program counter.
      * @param context workflow context
-     * @return next worklet
+     * @return next program counter
      * @throws WorkflowException workflow exception
      */
-    Worklet next(WorkflowContext context) throws WorkflowException;
+    ProgramCounter next(WorkflowContext context) throws WorkflowException;
+
+    /**
+     * Gets increased program coounter.
+     * @param pc program counter
+     * @return increased program counter
+     * @throws WorkflowException workflow exception
+     */
+    ProgramCounter increased(ProgramCounter pc) throws WorkflowException;
 
     /**
      * Returns instance of worklet.
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowContext.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowContext.java
index 12d4673..9f0b4fe 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowContext.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowContext.java
@@ -57,16 +57,16 @@
     public abstract void setState(WorkflowState state);
 
     /**
-     * Sets the current worklet of workflow context.
-     * @param worklet current worklet
+     * Sets the current program counter of workflow context.
+     * @param pc current program counter
      */
-    public abstract void setCurrent(Worklet worklet);
+    public abstract void setCurrent(ProgramCounter pc);
 
     /**
-     * Returns the class name of current worklet.
-     * @return the class name of current worklet
+     * Returns the current program counter of workflow.
+     * @return the current program counter of workflow
      */
-    public abstract String current();
+    public abstract ProgramCounter current();
 
     /**
      * Returns the cause string of exception state.
@@ -163,4 +163,13 @@
      * @return workplace store
      */
     public abstract WorkplaceStore workplaceStore();
+
+    /**
+     * Get service.
+     * @param serviceClass service class
+     * @param <T> service class type
+     * @return service reference
+     * @throws WorkflowException workflow exception
+     */
+    public abstract <T> T getService(Class<T> serviceClass) throws WorkflowException;
 }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowExecutionService.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowExecutionService.java
index ad9f731..9f609b3 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowExecutionService.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowExecutionService.java
@@ -30,6 +30,12 @@
     void execInitWorklet(WorkflowContext context);
 
     /**
+     * Evals workflow context.
+     * @param contextName the name of workflow context
+     */
+    void eval(String contextName);
+
+    /**
      * Triggers workflow event map.
      * @param event triggering event
      * @param generator event hint generation method reference
@@ -41,10 +47,9 @@
      * @param eventType event type (class name of event)
      * @param eventHint event hint value
      * @param contextName workflow context name to be called by this event map
-     * @param workletType worklet type to be called by this event map
+     * @param programCounterString worklet type to be called by this event map
      * @throws WorkflowException workflow exception
      */
     void registerEventMap(Class<? extends Event> eventType, String eventHint,
-                          String contextName, String workletType) throws WorkflowException;
-
+                          String contextName, String programCounterString) throws WorkflowException;
 }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/Worklet.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/Worklet.java
index 386fa38..c4b4fb2 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/Worklet.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/Worklet.java
@@ -23,7 +23,7 @@
  */
 public interface Worklet {
 
-    int MAX_WORKS = 1000;
+    int MAX_WORKS = 10000;
 
     /**
      * Returns tag name of worklet. class name is usually used.
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowCommand.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowCommand.java
index 3f96ba9..5c0f5de 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowCommand.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowCommand.java
@@ -21,23 +21,22 @@
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.workflow.api.DefaultWorkflowDescription;
+import org.onosproject.workflow.api.WorkflowContext;
+import org.onosproject.workflow.api.WorkflowExecutionService;
 import org.onosproject.workflow.api.WorkflowService;
 import org.onosproject.workflow.api.WorkflowException;
+import org.onosproject.workflow.api.WorkplaceStore;
 
-import java.util.Arrays;
 import java.util.Objects;
 
 @Service
 @Command(scope = "onos", name = "workflow", description = "workflow cli")
 public class WorkFlowCommand extends AbstractShellCommand {
 
-    @Argument(index = 0, name = "cmd", description = "command(invoke)", required = true)
+    @Argument(index = 0, name = "cmd", description = "command(invoke|eval)", required = true)
     private String cmd = null;
 
-    @Argument (index = 1, name = "id", description = "workflow id(URI)", required = true)
-    private String id = null;
-
-    @Argument (index = 2, name = "name", description = "workplace name", required = true)
+    @Argument(index = 1, name = "name", description = "workflow context name(workflow@workplace)", required = true)
     private String name = null;
 
     @Override
@@ -47,19 +46,26 @@
             return;
         }
 
+        if (Objects.isNull(name)) {
+            error("invalid workflow context name");
+            return;
+        }
+
+        String[] tokens = name.split("@");
+        if (tokens != null && tokens.length != 2) {
+            error("invalid workflow context name(workflow@workplace)");
+            return;
+        }
+
+        String workflowId = tokens[0];
+        String workplace = tokens[1];
+
         switch (cmd) {
             case "invoke":
-                if (Objects.isNull(id)) {
-                    error("invalid workflow id parameter");
-                    return;
-                }
-
-                if (Objects.isNull(name)) {
-                    error("invalid workplace name parameter");
-                    return;
-                }
-
-                invoke(id, name);
+                invoke(workflowId, workplace);
+                break;
+            case "eval":
+                eval(name);
                 break;
             default:
                 print("Unsupported cmd: " + cmd);
@@ -74,15 +80,33 @@
     private void invoke(String workflowId, String workplaceName) {
 
         WorkflowService service = get(WorkflowService.class);
-        DefaultWorkflowDescription wfDesc = DefaultWorkflowDescription.builder()
-                .workplaceName(workplaceName)
-                .id(workflowId)
-                .data(JsonNodeFactory.instance.objectNode())
-                .build();
         try {
+            DefaultWorkflowDescription wfDesc = DefaultWorkflowDescription.builder()
+                    .workplaceName(workplaceName)
+                    .id(workflowId)
+                    .data(JsonNodeFactory.instance.objectNode())
+                    .build();
+
             service.invokeWorkflow(wfDesc);
         } catch (WorkflowException e) {
-            error(e.getMessage() + ", trace: " + Arrays.asList(e.getStackTrace()));
+            error("Exception: ", e);
         }
     }
+
+    /**
+     * Evaluates workflow context.
+     * @param workflowContextName workflow context name
+     */
+    private void eval(String workflowContextName) {
+        WorkplaceStore storService = get(WorkplaceStore.class);
+        WorkflowExecutionService execService = get(WorkflowExecutionService.class);
+
+        WorkflowContext context = storService.getContext(workflowContextName);
+        if (context == null) {
+            error("failed to find workflow context {}", workflowContextName);
+            return;
+        }
+        execService.eval(workflowContextName);
+    }
+
 }
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowEventMapCommand.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowEventMapCommand.java
index e23e97d..5240252 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowEventMapCommand.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowEventMapCommand.java
@@ -51,7 +51,7 @@
                     try {
                         print(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(tree));
                     } catch (JsonProcessingException e) {
-                        error(e.getMessage() + ", trace: " + Arrays.asList(e.getStackTrace()));
+                        error("Exception: " + e.getMessage() + ", trace: " + Arrays.asList(e.getStackTrace()));
                     }
                     break;
 
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowStoreCommand.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowStoreCommand.java
index f5e795a..d1e37c8 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowStoreCommand.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowStoreCommand.java
@@ -32,7 +32,7 @@
     @Argument(index = 0, name = "cmd", description = "command(rm)", required = false)
     private String cmd = null;
 
-    @Argument (index = 1, name = "id", description = "workflow id(URI)", required = false)
+    @Argument(index = 1, name = "id", description = "workflow id(URI)", required = false)
     private String id = null;
 
     @Override
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowTestCommand.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowTestCommand.java
index b3c34ec..8d13508 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowTestCommand.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowTestCommand.java
@@ -16,6 +16,7 @@
 package org.onosproject.workflow.cli;
 
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
@@ -34,7 +35,7 @@
     @Argument(index = 0, name = "cmd", description = "command(invoke)", required = true)
     private String cmd = null;
 
-    @Argument (index = 1, name = "number", description = "number of test", required = true)
+    @Argument(index = 1, name = "number", description = "number of test", required = true)
     private String number = null;
 
     @Override
@@ -74,12 +75,16 @@
     private void invoke(String workflowId, String workplaceName) {
 
         WorkflowService service = get(WorkflowService.class);
-        DefaultWorkflowDescription wfDesc = DefaultWorkflowDescription.builder()
-                .workplaceName(workplaceName)
-                .id(workflowId)
-                .data(JsonNodeFactory.instance.objectNode())
-                .build();
+
+        ObjectNode dataModel = JsonNodeFactory.instance.objectNode();
+        dataModel.put("count", 0);
+
         try {
+            DefaultWorkflowDescription wfDesc = DefaultWorkflowDescription.builder()
+                    .workplaceName(workplaceName)
+                    .id(workflowId)
+                    .data(dataModel)
+                    .build();
             service.invokeWorkflow(wfDesc);
         } catch (WorkflowException e) {
             error(e.getMessage() + "trace: " + Arrays.asList(e.getStackTrace()));
@@ -94,6 +99,8 @@
         for (int i = 0; i <= num; i++) {
             String wpName = "test-" + i;
             invoke("sample.workflow-0", wpName);
+            invoke("sample.workflow-1", wpName);
+            invoke("sample.workflow-2", wpName);
         }
     }
 }
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkplaceStoreCommand.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkplaceStoreCommand.java
index db1a736..1b4ba8a 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkplaceStoreCommand.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkplaceStoreCommand.java
@@ -38,7 +38,7 @@
     @Argument(index = 0, name = "cmd", description = "command(add/rm/clear/print)", required = false)
     private String cmd = null;
 
-    @Argument (index = 1, name = "name", description = "workspace name", required = false)
+    @Argument(index = 1, name = "name", description = "workspace name", required = false)
     private String name = null;
 
     @Option(name = "-f", aliases = "--filter", description = "including filter",
@@ -97,10 +97,10 @@
      */
     private void addEmptyWorkplace(String name) {
         WorkflowService service = get(WorkflowService.class);
-        DefaultWorkplaceDescription wpDesc = DefaultWorkplaceDescription.builder()
-                .name(name)
-                .build();
         try {
+            DefaultWorkplaceDescription wpDesc = DefaultWorkplaceDescription.builder()
+                    .name(name)
+                    .build();
             service.createWorkplace(wpDesc);
         } catch (WorkflowException e) {
             error(e.getMessage() + ", trace: " + Arrays.asList(e.getStackTrace()));
@@ -125,10 +125,10 @@
      */
     private void rmWorkplace(String name) {
         WorkflowService service = get(WorkflowService.class);
-        DefaultWorkplaceDescription wpDesc = DefaultWorkplaceDescription.builder()
-                .name(name)
-                .build();
         try {
+            DefaultWorkplaceDescription wpDesc = DefaultWorkplaceDescription.builder()
+                    .name(name)
+                    .build();
             service.removeWorkplace(wpDesc);
         } catch (WorkflowException e) {
             error(e.getMessage() + ", trace: " + Arrays.asList(e.getStackTrace()));
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/DistributedContextEventMapTreeStore.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/DistributedContextEventMapTreeStore.java
index 1ee2cf0..1b725de 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/DistributedContextEventMapTreeStore.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/DistributedContextEventMapTreeStore.java
@@ -89,13 +89,13 @@
 
     @Override
     public void registerEventMap(String eventType, String eventHint,
-                                 String contextName, String workletType) throws WorkflowException {
+                                 String contextName, String programCounterString) throws WorkflowException {
         DocumentPath dpath = DocumentPath.from(Lists.newArrayList("root", eventType, eventHint, contextName));
         String currentWorkletType = completeVersioned(eventMapTree.get(dpath));
         if (currentWorkletType == null) {
-            complete(eventMapTree.createRecursive(dpath, workletType));
+            complete(eventMapTree.createRecursive(dpath, programCounterString));
         } else {
-            complete(eventMapTree.replace(dpath, workletType, currentWorkletType));
+            complete(eventMapTree.replace(dpath, programCounterString, currentWorkletType));
         }
     }
 
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/DistributedWorkplaceStore.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/DistributedWorkplaceStore.java
index 31fe044..12de3f0 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/DistributedWorkplaceStore.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/DistributedWorkplaceStore.java
@@ -52,6 +52,7 @@
 import org.onosproject.workflow.api.DefaultWorkplace;
 import org.onosproject.workflow.api.DefaultWorkflowContext;
 import org.onosproject.workflow.api.JsonDataModelTree;
+import org.onosproject.workflow.api.ProgramCounter;
 import org.onosproject.workflow.api.SystemWorkflowContext;
 import org.onosproject.workflow.api.WorkflowContext;
 import org.onosproject.workflow.api.WorkflowData;
@@ -119,6 +120,7 @@
                 .register(DefaultWorkflowContext.class)
                 .register(SystemWorkflowContext.class)
                 .register(WorkflowState.class)
+                .register(ProgramCounter.class)
                 .register(DataModelTree.class)
                 .register(JsonDataModelTree.class)
                 .register(List.class)
@@ -311,7 +313,7 @@
             WorkflowContext newContext = (WorkflowContext) Versioned.valueOrNull(event.newValue());
             WorkflowContext oldContext = (WorkflowContext) Versioned.valueOrNull(event.oldValue());
 
-            log.info("WorkflowContext event: {}", event);
+            log.debug("WorkflowContext event: {}", event);
             switch (event.type()) {
                 case INSERT:
                     insert(newContext);
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkFlowEngine.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkFlowEngine.java
index e4d545d..237f70e 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkFlowEngine.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkFlowEngine.java
@@ -22,10 +22,13 @@
 import org.onosproject.cluster.NodeId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.store.service.StorageException;
 import org.onosproject.workflow.api.DefaultWorkplace;
 import org.onosproject.workflow.api.EventHintSupplier;
 import org.onosproject.workflow.api.EventTask;
+import org.onosproject.workflow.api.JsonDataModelInjector;
 import org.onosproject.workflow.api.JsonDataModelTree;
+import org.onosproject.workflow.api.ProgramCounter;
 import org.onosproject.workflow.api.SystemWorkflowContext;
 import org.onosproject.workflow.api.EventTimeoutTask;
 import org.onosproject.workflow.api.TimeoutTask;
@@ -122,6 +125,8 @@
 
     private TimerChain timerChain = new TimerChain();
 
+    private JsonDataModelInjector dataModelInjector = new JsonDataModelInjector();
+
     public static final String APPID = "org.onosproject.workflow";
     private ApplicationId appId;
     private NodeId localNodeId;
@@ -177,11 +182,20 @@
         try {
             Worklet initWorklet = workflow.init(context);
             if (initWorklet != null) {
+
+                log.info("{} worklet.process:{}", context.name(), initWorklet.tag());
+                log.trace("{} context: {}", context.name(), context);
+
+                dataModelInjector.inject(initWorklet, context);
                 initWorklet.process(context);
+                dataModelInjector.inhale(initWorklet, context);
+
+                log.info("{} worklet.process(done): {}", context.name(), initWorklet.tag());
+                log.trace("{} context: {}", context.name(), context);
             }
 
         } catch (WorkflowException e) {
-            log.error("Exception: {}, trace: {}", e, Lists.newArrayList(e.getStackTrace()));
+            log.error("Exception: ", e);
             context.setCause(e.getMessage());
             context.setState(WorkflowState.EXCEPTION);
             workplaceStore.commitContext(context.name(), context, false);
@@ -192,6 +206,20 @@
     }
 
     @Override
+    public void eval(String contextName) {
+
+        final WorkflowContext latestContext = workplaceStore.getContext(contextName);
+        if (latestContext == null) {
+            log.error("Invalid workflow context {}", contextName);
+            return;
+        }
+
+        initWorkletExecution(latestContext);
+
+        workplaceStore.commitContext(latestContext.name(), latestContext, true);
+    }
+
+    @Override
     public void eventMapTrigger(Event event, EventHintSupplier supplier) {
 
         if (event.subject() instanceof SystemWorkflowContext) {
@@ -204,7 +232,7 @@
         try {
             eventHint = supplier.apply(event);
         } catch (Throwable e) {
-            log.error("Exception: {}, trace: {}", e, Lists.newArrayList(e.getStackTrace()));
+            log.error("Exception: ", e);
             return;
         }
         if (eventHint == null) {
@@ -216,7 +244,7 @@
         try {
             eventMap = eventMapStore.getEventMap(event.getClass().getName(), eventHint);
         } catch (WorkflowException e) {
-            log.error("Exception: {}, trace: {}", e, Lists.newArrayList(e.getStackTrace()));
+            log.error("Exception: ", e);
             return;
         }
 
@@ -228,28 +256,43 @@
 
         for (Map.Entry<String, String> entry : eventMap.entrySet()) {
             String contextName = entry.getKey();
-            String workletType = entry.getValue();
+            String strProgramCounter = entry.getValue();
+            ProgramCounter pc;
+            try {
+                pc = ProgramCounter.valueOf(strProgramCounter);
+            } catch (IllegalArgumentException e) {
+                log.error("Exception: ", e);
+                return;
+            }
+
             WorkflowContext context = workplaceStore.getContext(contextName);
             if (Objects.isNull(context)) {
                 log.info("Invalid context: {}, event: {}", contextName, event);
                 continue;
             }
-            EventTask eventtask = EventTask.builder()
+            EventTask eventtask = null;
+            try {
+                eventtask = EventTask.builder()
                     .event(event)
                     .eventHint(eventHint)
                     .context(context)
-                    .workletType(workletType)
+                    .programCounter(pc)
                     .build();
+            } catch (WorkflowException e) {
+                log.error("Exception: ", e);
+            }
 
-            log.info("eventtaskAccumulator.add: task: {}", eventtask);
-            eventtaskAccumulator.add(eventtask);
+            log.debug("eventtaskAccumulator.add: task: {}", eventtask);
+            if (!Objects.isNull(eventtask)) {
+                eventtaskAccumulator.add(eventtask);
+            }
         }
     }
 
     @Override
     public void registerEventMap(Class<? extends Event> eventType, String eventHint,
-                                 String contextName, String workletType) throws WorkflowException {
-        eventMapStore.registerEventMap(eventType.getName(), eventHint, contextName, workletType);
+                                 String contextName, String programCounterString) throws WorkflowException {
+        eventMapStore.registerEventMap(eventType.getName(), eventHint, contextName, programCounterString);
         for (int i = 0; i < MAX_REGISTER_EVENTMAP_WAITS; i++) {
             Map<String, String> eventMap = eventMapStore.getEventMap(eventType.getName(), eventHint);
             if (eventMap != null && eventMap.containsKey(contextName)) {
@@ -259,6 +302,7 @@
                 log.info("sleep {}", i);
                 Thread.sleep(10L * (i + 1));
             } catch (InterruptedException e) {
+                log.error("Exception: ", e);
                 Thread.currentThread().interrupt();
             }
         }
@@ -278,7 +322,7 @@
             case INSERT:
             case UPDATE:
                 if (dataModelContainer.triggerNext()) {
-                    log.info("workflowAccumulator.add: {}", dataModelContainer);
+                    log.debug("workflowAccumulator.add: {}", dataModelContainer);
                     workflowAccumulator.add(dataModelContainer);
                 } else {
                     log.debug("pass-workflowAccumulator.add: {}", dataModelContainer);
@@ -430,33 +474,60 @@
         }
 
         try {
-            Worklet worklet = workflow.getWorkletInstance(task.workletType());
-            if (!Objects.equals(latestContext.current(), worklet.tag())) {
+            if (!Objects.equals(latestContext.current(), task.programCounter())) {
                 log.error("Current worklet({}) is not mismatched with task work({}). Ignored.",
-                        latestContext.current(), worklet.tag());
+                        latestContext.current(), task.programCounter());
                 return task;
             }
 
-            if (worklet == Worklet.Common.COMPLETED || worklet == Worklet.Common.INIT) {
+            Worklet worklet = workflow.getWorkletInstance(task.programCounter().workletType());
+            if (Worklet.Common.COMPLETED.equals(worklet) || Worklet.Common.INIT.equals(worklet)) {
                 log.error("Current worklet is {}, Ignored", worklet);
                 return task;
             }
 
             initWorkletExecution(latestContext);
 
-            log.info("processHandlerTask.isCompleted-task:{}, latest:{}", task, latestContext);
-            if (worklet.isCompleted(latestContext, task.event())) {
+            log.info("{} worklet.isCompleted:{}", latestContext.name(), worklet.tag());
+            log.trace("{} task:{}, context: {}", latestContext.name(), task, latestContext);
+
+            dataModelInjector.inject(worklet, latestContext);
+            boolean completed = worklet.isCompleted(latestContext, task.event());
+            dataModelInjector.inhale(worklet, latestContext);
+
+            if (completed) {
+
+                log.info("{} worklet.isCompleted(true):{}", latestContext.name(), worklet.tag());
+                log.trace("{} task:{}, context: {}", latestContext.name(), task, latestContext);
+
                 eventMapStore.unregisterEventMap(
                         task.eventType(), task.eventHint(), latestContext.name());
 
+                //completed case
+                // increase program counter
+                ProgramCounter pc = latestContext.current();
+                latestContext.setCurrent(workflow.increased(pc));
+
                 workplaceStore.commitContext(latestContext.name(), latestContext, true);
                 return null;
             } else {
+
+                log.info("{} worklet.isCompleted(false):{}", latestContext.name(), worklet.tag());
+                log.trace("{} task:{}, context: {}", latestContext.name(), task, latestContext);
+
                 workplaceStore.commitContext(latestContext.name(), latestContext, false);
             }
 
         } catch (WorkflowException e) {
-            log.error("Exception: {}, trace: {}", e, Lists.newArrayList(e.getStackTrace()));
+            log.error("Exception: ", e);
+            latestContext.setCause(e.getMessage());
+            latestContext.setState(WorkflowState.EXCEPTION);
+            workplaceStore.commitContext(latestContext.name(), latestContext, false);
+        } catch (StorageException e) {
+            log.error("Exception: ", e);
+            // StorageException does not commit context.
+        } catch (Exception e) {
+            log.error("Exception: ", e);
             latestContext.setCause(e.getMessage());
             latestContext.setState(WorkflowState.EXCEPTION);
             workplaceStore.commitContext(latestContext.name(), latestContext, false);
@@ -491,7 +562,7 @@
 
         log.debug("execEventTimeoutTask- task: {}, hash: {}", task, stringHash(task.context().distributor()));
 
-        WorkflowContext context = task.context();
+        WorkflowContext context = (WorkflowContext) (task.context());
         Workflow workflow = workflowStore.get(context.workflowId());
         if (workflow == null) {
             log.error("execEventTimeoutTask: Invalid workflow {}", context.workflowId());
@@ -505,13 +576,13 @@
         }
 
         try {
-            Worklet worklet = workflow.getWorkletInstance(task.workletType());
-            if (!Objects.equals(latestContext.current(), worklet.tag())) {
+            if (!Objects.equals(latestContext.current(), task.programCounter())) {
                 log.error("execEventTimeoutTask: Current worklet({}) is not mismatched with task work({}). Ignored.",
-                        latestContext.current(), worklet.tag());
+                        latestContext.current(), task.programCounter());
                 return task;
             }
 
+            Worklet worklet = workflow.getWorkletInstance(task.programCounter().workletType());
             if (worklet == Worklet.Common.COMPLETED || worklet == Worklet.Common.INIT) {
                 log.error("execEventTimeoutTask: Current worklet is {}, Ignored", worklet);
                 return task;
@@ -519,15 +590,32 @@
 
             initWorkletExecution(latestContext);
 
-            log.info("execEventTimeoutTask.timeout-task:{}, latest:{}", task, latestContext);
             eventMapStore.unregisterEventMap(
                     task.eventType(), task.eventHint(), latestContext.name());
 
+            log.info("{} worklet.timeout(for event):{}", latestContext.name(), worklet.tag());
+            log.trace("{} task:{}, context: {}", latestContext.name(), task, latestContext);
+
+            dataModelInjector.inject(worklet, latestContext);
             worklet.timeout(latestContext);
+            dataModelInjector.inhale(worklet, latestContext);
+
+            log.info("{} worklet.timeout(for event)(done):{}", latestContext.name(), worklet.tag());
+            log.trace("{} task:{}, context: {}", latestContext.name(), task, latestContext);
+
+
             workplaceStore.commitContext(latestContext.name(), latestContext, latestContext.triggerNext());
 
         } catch (WorkflowException e) {
-            log.error("Exception: {}, trace: {}", e, Lists.newArrayList(e.getStackTrace()));
+            log.error("Exception: ", e);
+            latestContext.setCause(e.getMessage());
+            latestContext.setState(WorkflowState.EXCEPTION);
+            workplaceStore.commitContext(latestContext.name(), latestContext, false);
+        } catch (StorageException e) {
+            log.error("Exception: ", e);
+            // StorageException does not commit context.
+        } catch (Exception e) {
+            log.error("Exception: ", e);
             latestContext.setCause(e.getMessage());
             latestContext.setState(WorkflowState.EXCEPTION);
             workplaceStore.commitContext(latestContext.name(), latestContext, false);
@@ -559,13 +647,13 @@
         }
 
         try {
-            Worklet worklet = workflow.getWorkletInstance(task.workletType());
-            if (!Objects.equals(latestContext.current(), worklet.tag())) {
+            if (!Objects.equals(latestContext.current(), task.programCounter())) {
                 log.error("execTimeoutTask: Current worklet({}) is not mismatched with task work({}). Ignored.",
-                        latestContext.current(), worklet.tag());
+                        latestContext.current(), task.programCounter());
                 return task;
             }
 
+            Worklet worklet = workflow.getWorkletInstance(task.programCounter().workletType());
             if (worklet == Worklet.Common.COMPLETED || worklet == Worklet.Common.INIT) {
                 log.error("execTimeoutTask: Current worklet is {}, Ignored", worklet);
                 return task;
@@ -573,11 +661,28 @@
 
             initWorkletExecution(latestContext);
 
+            log.info("{} worklet.timeout:{}", latestContext.name(), worklet.tag());
+            log.trace("{} context: {}", latestContext.name(), latestContext);
+
+            dataModelInjector.inject(worklet, latestContext);
             worklet.timeout(latestContext);
+            dataModelInjector.inhale(worklet, latestContext);
+
+            log.info("{} worklet.timeout(done):{}", latestContext.name(), worklet.tag());
+            log.trace("{} context: {}", latestContext.name(), latestContext);
+
             workplaceStore.commitContext(latestContext.name(), latestContext, latestContext.triggerNext());
 
         } catch (WorkflowException e) {
-            log.error("Exception: {}, trace: {}", e, Lists.newArrayList(e.getStackTrace()));
+            log.error("Exception: ", e);
+            latestContext.setCause(e.getMessage());
+            latestContext.setState(WorkflowState.EXCEPTION);
+            workplaceStore.commitContext(latestContext.name(), latestContext, false);
+        } catch (StorageException e) {
+            log.error("Exception: ", e);
+            // StorageException does not commit context.
+        } catch (Exception e) {
+            log.error("Exception: ", e);
             latestContext.setCause(e.getMessage());
             latestContext.setState(WorkflowState.EXCEPTION);
             workplaceStore.commitContext(latestContext.name(), latestContext, false);
@@ -651,14 +756,15 @@
         initWorkletExecution(latestContext);
 
         try {
-            final Worklet worklet = workflow.next(latestContext);
+            final ProgramCounter pc = workflow.next(latestContext);
+            final Worklet worklet = workflow.getWorkletInstance(pc.workletType());
 
             if (worklet == Worklet.Common.INIT) {
                 log.error("workflow.next gave INIT. It cannot be executed (context: {})", context.name());
                 return latestContext;
             }
 
-            latestContext.setCurrent(worklet);
+            latestContext.setCurrent(pc);
             if (worklet == Worklet.Common.COMPLETED) {
 
                 if (workflow.attributes().contains(REMOVE_AFTER_COMPLETE)) {
@@ -671,8 +777,16 @@
                 }
             }
 
-            log.info("execWorkflowContext.process:{}, {}", worklet.tag(), latestContext);
+            log.info("{} worklet.process:{}", latestContext.name(), worklet.tag());
+            log.trace("{} context: {}", latestContext.name(), latestContext);
+
+            dataModelInjector.inject(worklet, latestContext);
             worklet.process(latestContext);
+            dataModelInjector.inhale(worklet, latestContext);
+
+            log.info("{} worklet.process(done): {}", latestContext.name(), worklet.tag());
+            log.trace("{} context: {}", latestContext.name(), latestContext);
+
 
             if (latestContext.completionEventType() != null) {
                 if (latestContext.completionEventGenerator() == null) {
@@ -683,14 +797,14 @@
                 }
 
                 registerEventMap(latestContext.completionEventType(), latestContext.completionEventHint(),
-                        latestContext.name(), worklet.tag());
+                        latestContext.name(), pc.toString());
 
                 latestContext.completionEventGenerator().apply();
 
                 if (latestContext.completionEventTimeout() != 0L) {
                     final EventTimeoutTask eventTimeoutTask = EventTimeoutTask.builder()
                             .context(latestContext)
-                            .workletType(worklet.tag())
+                            .programCounter(pc)
                             .eventType(latestContext.completionEventType().getName())
                             .eventHint(latestContext.completionEventHint())
                             .build();
@@ -703,20 +817,32 @@
                 if (latestContext.completionEventTimeout() != 0L) {
                     final TimeoutTask timeoutTask = TimeoutTask.builder()
                             .context(latestContext)
-                            .workletType(worklet.tag())
+                            .programCounter(pc)
                             .build();
 
                     timerChain.schedule(latestContext.completionEventTimeout(),
                             () -> {
                                 eventtaskAccumulator.add(timeoutTask);
                             });
+                } else {
+                    //completed case
+                    // increase program counter
+                    latestContext.setCurrent(workflow.increased(pc));
                 }
             }
 
             workplaceStore.commitContext(latestContext.name(), latestContext, latestContext.triggerNext());
 
         } catch (WorkflowException e) {
-            log.error("Exception: {}, trace: {}", e, Lists.newArrayList(e.getStackTrace()));
+            log.error("Exception: ", e);
+            latestContext.setCause(e.getMessage());
+            latestContext.setState(WorkflowState.EXCEPTION);
+            workplaceStore.commitContext(latestContext.name(), latestContext, false);
+        } catch (StorageException e) {
+            log.error("Exception: ", e);
+            // StorageException does not commit context.
+        } catch (Exception e) {
+            log.error("Exception: ", e);
             latestContext.setCause(e.getMessage());
             latestContext.setState(WorkflowState.EXCEPTION);
             workplaceStore.commitContext(latestContext.name(), latestContext, false);
@@ -735,4 +861,4 @@
         return null;
     }
 
-}
\ No newline at end of file
+}
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkflowNetConfigListener.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkflowNetConfigListener.java
index 59a57e9..bb7f875 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkflowNetConfigListener.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkflowNetConfigListener.java
@@ -68,7 +68,7 @@
 
     @Override
     public boolean isRelevant(NetworkConfigEvent event) {
-        return true;
+        return event.config().isPresent() && event.config().get() instanceof WorkflowNetConfig;
     }
 
     @Override
@@ -116,7 +116,7 @@
                     rpcMap.get(rpc.op()).apply(this.workflowService, rpc);
                 }
             } catch (WorkflowException e) {
-                e.printStackTrace();
+                log.error("Exception: ", e);
             }
         }
     }
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkplaceWorkflow.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkplaceWorkflow.java
index af62aaa..219436c 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkplaceWorkflow.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkplaceWorkflow.java
@@ -143,6 +143,15 @@
                 return false;
             }
         }
+
+        @Override
+        public void timeout(WorkflowContext context) throws WorkflowException {
+            if (!isNext(context)) {
+                context.completed(); //Complete the job of worklet by timeout
+            } else {
+                super.timeout(context);
+            }
+        }
     }
 
     public static class CreateWorkflowContext extends AbsWorkflowWorklet {
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/example/SampleWorkflow.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/example/SampleWorkflow.java
index 20b3771..9d4e7b2 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/example/SampleWorkflow.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/example/SampleWorkflow.java
@@ -22,6 +22,7 @@
 import org.onosproject.workflow.api.AbstractWorklet;
 import org.onosproject.workflow.api.DataModelTree;
 import org.onosproject.workflow.api.ImmutableListWorkflow;
+import org.onosproject.workflow.api.JsonDataModel;
 import org.onosproject.workflow.api.JsonDataModelTree;
 import org.onosproject.workflow.api.Workflow;
 import org.onosproject.workflow.api.WorkflowContext;
@@ -122,7 +123,8 @@
      */
     public abstract static class AbsSampleWorklet extends AbstractWorklet {
 
-        protected static final String SAMPLE_DATAMODEL_PTR = "/sample/job";
+        protected static final String MODEL_SAMPLE_JOB = "/sample/job";
+        protected static final String MODEL_COUNT = "/count";
 
         /**
          * Constructor for sample worklet.
@@ -142,10 +144,10 @@
             JsonDataModelTree tree = (JsonDataModelTree) context.data();
             JsonNode params = tree.root();
 
-            if (params.at(SAMPLE_DATAMODEL_PTR).getNodeType() == JsonNodeType.MISSING) {
-                tree.alloc(SAMPLE_DATAMODEL_PTR, DataModelTree.Nodetype.MAP);
+            if (params.at(MODEL_SAMPLE_JOB).getNodeType() == JsonNodeType.MISSING) {
+                tree.alloc(MODEL_SAMPLE_JOB, DataModelTree.Nodetype.MAP);
             }
-            return (ObjectNode) params.at(SAMPLE_DATAMODEL_PTR);
+            return (ObjectNode) params.at(MODEL_SAMPLE_JOB);
         }
 
         /**
@@ -156,7 +158,7 @@
          */
         protected ObjectNode getDataModel(WorkflowContext context) throws WorkflowException {
             DataModelTree tree = context.data();
-            return ((JsonDataModelTree) tree.subtree(SAMPLE_DATAMODEL_PTR)).rootObject();
+            return ((JsonDataModelTree) tree.subtree(MODEL_SAMPLE_JOB)).rootObject();
         }
 
         /**
@@ -167,6 +169,7 @@
             try {
                 Thread.sleep(ms);
             } catch (InterruptedException e) {
+                log.error("Exception: ", e);
                 Thread.currentThread().interrupt();
             }
         }
@@ -176,6 +179,10 @@
      * Class for sample worklet-1.
      */
     public static class SampleWorklet1 extends AbsSampleWorklet {
+
+        @JsonDataModel(path = MODEL_COUNT, type = Integer.class)
+        Integer intCount;
+
         @Override
         public void process(WorkflowContext context) throws WorkflowException {
             ObjectNode node = getDataModel(context);
@@ -200,12 +207,17 @@
      * Class for sample worklet-2 (using timeout).
      */
     public static class SampleWorklet2 extends AbsSampleWorklet {
+
+        @JsonDataModel(path = MODEL_COUNT, type = Integer.class)
+        Integer intCount;
+
         @Override
         public void process(WorkflowContext context) throws WorkflowException {
             ObjectNode node = getDataModel(context);
             node.put("work2", "done");
             log.info("workflow-process {}-{}", context.workplaceName(), this.getClass().getSimpleName());
             sleep(50);
+            intCount++;
 
             context.waitFor(50L); //Timeout will happen after 50 milli seconds.
         }
@@ -225,13 +237,17 @@
     }
 
     public static class SampleWorklet3 extends AbsSampleWorklet {
+
+        @JsonDataModel(path = MODEL_COUNT, type = Integer.class)
+        Integer intCount;
+
         @Override
         public void process(WorkflowContext context) throws WorkflowException {
             ObjectNode node = getDataModel(context);
             node.put("work3", "done");
             log.info("workflow-process {}-{}", context.workplaceName(), this.getClass().getSimpleName());
             sleep(10);
-
+            intCount++;
             context.completed();
         }
 
@@ -245,13 +261,17 @@
     }
 
     public static class SampleWorklet4 extends AbsSampleWorklet {
+
+        @JsonDataModel(path = MODEL_COUNT, type = Integer.class)
+        Integer intCount;
+
         @Override
         public void process(WorkflowContext context) throws WorkflowException {
             ObjectNode node = getDataModel(context);
             node.put("work4", "done");
             log.info("workflow-process {}-{}", context.workplaceName(), this.getClass().getSimpleName());
             sleep(10);
-
+            intCount++;
             context.completed();
         }
 
@@ -265,13 +285,17 @@
     }
 
     public static class SampleWorklet5 extends AbsSampleWorklet {
+
+        @JsonDataModel(path = MODEL_COUNT, type = Integer.class)
+        Integer intCount;
+
         @Override
         public void process(WorkflowContext context) throws WorkflowException {
             ObjectNode node = getDataModel(context);
             node.put("work5", "done");
             log.info("workflow-process {}-{}", context.workplaceName(), this.getClass().getSimpleName());
             sleep(10);
-
+            intCount++;
             context.completed();
         }
 
@@ -284,4 +308,4 @@
         }
     }
 
-}
\ No newline at end of file
+}