Add TAPI object resolver and DCS-dependent data provider ( to update cache )

- refactor TapiInstanceBuilder and add some util method
- add skeleton code for tapi-common get RPCs

Change-Id: I548623ad29f9b72b425713720c5081d790b42bd2
diff --git a/apps/odtn/service/src/main/java/org/onosproject/odtn/impl/ServiceApplicationComponent.java b/apps/odtn/service/src/main/java/org/onosproject/odtn/impl/ServiceApplicationComponent.java
index 60cc41a..2a92137 100644
--- a/apps/odtn/service/src/main/java/org/onosproject/odtn/impl/ServiceApplicationComponent.java
+++ b/apps/odtn/service/src/main/java/org/onosproject/odtn/impl/ServiceApplicationComponent.java
@@ -27,6 +27,11 @@
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.config.DynamicConfigEvent;
+import org.onosproject.config.DynamicConfigListener;
+import org.onosproject.config.DynamicConfigService;
+import org.onosproject.config.FailedException;
+import org.onosproject.config.Filter;
 import org.onosproject.net.Link;
 import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.net.device.DeviceEvent;
@@ -35,7 +40,14 @@
 import org.onosproject.net.link.LinkEvent;
 import org.onosproject.net.link.LinkListener;
 import org.onosproject.net.link.LinkService;
+import org.onosproject.odtn.internal.DcsBasedTapiDataProducer;
+import org.onosproject.odtn.internal.TapiDataProducer;
+import org.onosproject.odtn.internal.TapiResolver;
 import org.onosproject.odtn.internal.TapiTopologyManager;
+import org.onosproject.yang.gen.v1.tapicommon.rev20180307.TapiCommonService;
+import org.onosproject.yang.gen.v1.tapicommon.rev20180307.tapicommon.getserviceinterfacepointlist.DefaultGetServiceInterfacePointListOutput;
+import org.onosproject.yang.model.NodeKey;
+import org.onosproject.yang.model.SchemaId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -67,6 +79,10 @@
 import org.onosproject.yang.model.SchemaContextProvider;
 import org.onosproject.yang.runtime.YangRuntimeService;
 
+import static org.onosproject.config.DynamicConfigEvent.Type.NODE_ADDED;
+import static org.onosproject.config.DynamicConfigEvent.Type.NODE_DELETED;
+import static org.onosproject.odtn.utils.YangToolUtil.toDataNode;
+
 /**
  * OSGi Component for ODTN Service application.
  */
@@ -75,8 +91,8 @@
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
-//    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-//    protected DynamicConfigService dynConfigService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DynamicConfigService dynConfigService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DeviceService deviceService;
@@ -102,35 +118,44 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected TapiTopologyManager tapiTopologyManager;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected TapiResolver resolver;
+
     // Listener for events from the DCS
-//    private final DynamicConfigListener dynamicConfigServiceListener =
-//            new InternalDynamicConfigListener();
+    private final DynamicConfigListener dynamicConfigServiceListener =
+            new InternalDynamicConfigListener();
 
     private DeviceListener deviceListener = new InternalDeviceListener();
     private final LinkListener linkListener = new InternalLinkListener();
+    private TapiDataProducer dataProvider = new DcsBasedTapiDataProducer();
 
     // Rpc Service for TAPI Connectivity
     private final RpcService rpcTapiConnectivity =
             new TapiConnectivityRpc();
 
+    private final RpcService rpcTapiCommon =
+            new TapiCommonRpc();
+
 
     @Activate
     protected void activate() {
         log.info("Started");
-//        dynConfigService.addListener(dynamicConfigServiceListener);
+        dynConfigService.addListener(dynamicConfigServiceListener);
         deviceService.addListener(deviceListener);
         linkService.addListener(linkListener);
         rpcRegistry.registerRpcService(rpcTapiConnectivity);
+        rpcRegistry.registerRpcService(rpcTapiCommon);
     }
 
 
     @Deactivate
     protected void deactivate() {
         log.info("Stopped");
+        rpcRegistry.unregisterRpcService(rpcTapiCommon);
         rpcRegistry.unregisterRpcService(rpcTapiConnectivity);
         linkService.removeListener(linkListener);
         deviceService.removeListener(deviceListener);
-//        dynConfigService.removeListener(dynamicConfigServiceListener);
+        dynConfigService.removeListener(dynamicConfigServiceListener);
     }
 
 
@@ -196,100 +221,103 @@
         }
     }
 
-//    /**
-//     * Representation of internal listener, listening for dynamic config event.
-//     */
-//    private class InternalDynamicConfigListener implements DynamicConfigListener {
-//
-//        /**
-//         * Check if the DCS event should be further processed.
-//         *
-//         * @param event config event
-//         * @return true if event is supported; false otherwise
-//         */
-//        @Override
-//        public boolean isRelevant(DynamicConfigEvent event) {
-//            // Only care about add and delete
-//            if ((event.type() != NODE_ADDED) &&
-//                    (event.type() != NODE_DELETED)) {
-//                return false;
-//            }
-//            return true;
-//        }
-//
-//
-//        /**
-//         * Process an Event from the Dynamic Configuration Store.
-//         *
-//         * @param event config event
-//         */
-//        @Override
-//        public void event(DynamicConfigEvent event) {
-//            ResourceId rsId = event.subject();
-//            DataNode node;
-//            try {
-//                Filter filter = Filter.builder().addCriteria(rsId).build();
-//                node = dynConfigService.readNode(rsId, filter);
-//            } catch (FailedException e) {
-//                node = null;
-//            }
-//            switch (event.type()) {
-//                case NODE_ADDED:
-//                    onDcsNodeAdded(rsId, node);
-//                    break;
-//
-//                case NODE_DELETED:
-//                    onDcsNodeDeleted(node);
-//                    break;
-//
-//                default:
-//                    log.warn("Unknown Event", event.type());
-//                    break;
-//            }
-//        }
-//
-//
-//        /**
-//         * Process the event that a node has been added to the DCS.
-//         *
-//         * @param rsId ResourceId of the added node
-//         * @param node added node. Access the key and value
-//         */
-//        private void onDcsNodeAdded(ResourceId rsId, DataNode node) {
-//            switch (node.type()) {
-//                case SINGLE_INSTANCE_NODE:
-//                    break;
-//                case MULTI_INSTANCE_NODE:
-//                    break;
-//                case SINGLE_INSTANCE_LEAF_VALUE_NODE:
-//                    break;
-//                case MULTI_INSTANCE_LEAF_VALUE_NODE:
-//                    break;
-//                default:
-//                    break;
-//            }
-//
-//            NodeKey dataNodeKey = node.key();
-//            SchemaId schemaId = dataNodeKey.schemaId();
+    /**
+     * Representation of internal listener, listening for dynamic config event.
+     */
+    private class InternalDynamicConfigListener implements DynamicConfigListener {
+
+        /**
+         * Check if the DCS event should be further processed.
+         *
+         * @param event config event
+         * @return true if event is supported; false otherwise
+         */
+        @Override
+        public boolean isRelevant(DynamicConfigEvent event) {
+            // Only care about add and delete
+            if ((event.type() != NODE_ADDED) &&
+                    (event.type() != NODE_DELETED)) {
+                return false;
+            }
+            return true;
+        }
+
+
+        /**
+         * Process an Event from the Dynamic Configuration Store.
+         *
+         * @param event config event
+         */
+        @Override
+        public void event(DynamicConfigEvent event) {
+            resolver.makeDirty();
+
+            ResourceId rsId = event.subject();
+            DataNode node;
+            try {
+                Filter filter = Filter.builder().addCriteria(rsId).build();
+                node = dynConfigService.readNode(rsId, filter);
+            } catch (FailedException e) {
+                node = null;
+            }
+            switch (event.type()) {
+                case NODE_ADDED:
+                    onDcsNodeAdded(rsId, node);
+                    break;
+
+                case NODE_DELETED:
+                    onDcsNodeDeleted(node);
+                    break;
+
+                default:
+                    log.warn("Unknown Event", event.type());
+                    break;
+            }
+        }
+
+
+        /**
+         * Process the event that a node has been added to the DCS.
+         *
+         * @param rsId ResourceId of the added node
+         * @param node added node. Access the key and value
+         */
+        private void onDcsNodeAdded(ResourceId rsId, DataNode node) {
+
+            switch (node.type()) {
+                case SINGLE_INSTANCE_NODE:
+                    break;
+                case MULTI_INSTANCE_NODE:
+                    break;
+                case SINGLE_INSTANCE_LEAF_VALUE_NODE:
+                    break;
+                case MULTI_INSTANCE_LEAF_VALUE_NODE:
+                    break;
+                default:
+                    break;
+            }
+
+            NodeKey dataNodeKey = node.key();
+            SchemaId schemaId = dataNodeKey.schemaId();
+
+            // Consolidate events
 //            if (!schemaId.namespace().contains("tapi")) {
 //                return;
 //            }
-//
-//            // Consolidate events
 //            log.info("namespace {}", schemaId.namespace());
-//        }
-//
-//
-//        /**
-//         * Process the event that a node has been deleted from the DCS.
-//         *
-//         * @param dataNode data node
-//         */
-//        private void onDcsNodeDeleted(DataNode dataNode) {
-//            // TODO: Implement release logic
-//        }
-//
-//    }
+        }
+
+
+        /**
+         * Process the event that a node has been deleted from the DCS.
+         *
+         * @param dataNode data node
+         */
+        private void onDcsNodeDeleted(DataNode dataNode) {
+            // TODO: Implement release logic
+        }
+
+    }
 
 
     private class TapiConnectivityRpc implements TapiConnectivityService {
@@ -409,4 +437,51 @@
 
 
     }
+
+
+    private class TapiCommonRpc implements TapiCommonService {
+
+        /**
+         * Service interface of getServiceInterfacePointDetails.
+         *
+         * @param rpcInput input of service interface getServiceInterfacePointDetails
+         * @return rpcOutput output of service interface getServiceInterfacePointDetails
+         */
+        @Override
+        public RpcOutput getServiceInterfacePointDetails(RpcInput rpcInput) {
+            return new RpcOutput(RpcOutput.Status.RPC_FAILURE, null);
+        }
+
+        /**
+         * Service interface of getServiceInterfacePointList.
+         *
+         * @param rpcInput input of service interface getServiceInterfacePointList
+         * @return rpcOutput output of service interface getServiceInterfacePointList
+         */
+        @Override
+        public RpcOutput getServiceInterfacePointList(RpcInput rpcInput) {
+
+            DataNode data = rpcInput.data();
+            ResourceId rid = rpcInput.id();
+
+            log.info("RpcInput Data {}", data);
+            log.info("RpcInput ResourceId {}", rid);
+
+            DefaultGetServiceInterfacePointListOutput node = new DefaultGetServiceInterfacePointListOutput();
+            DataNode dnode = toDataNode(node);
+
+            return new RpcOutput(RpcOutput.Status.RPC_SUCCESS, dnode);
+        }
+
+        /**
+         * Service interface of updateServiceInterfacePoint.
+q         *
+         * @param rpcInput input of service interface updateServiceInterfacePoint
+         * @return rpcOutput output of service interface updateServiceInterfacePoint
+         */
+        @Override
+        public RpcOutput updateServiceInterfacePoint(RpcInput rpcInput) {
+            return new RpcOutput(RpcOutput.Status.RPC_FAILURE, null);
+        }
+    }
 }
diff --git a/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/DcsBasedTapiDataProducer.java b/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/DcsBasedTapiDataProducer.java
new file mode 100644
index 0000000..5f6b96c
--- /dev/null
+++ b/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/DcsBasedTapiDataProducer.java
@@ -0,0 +1,162 @@
+/*
+ * 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.odtn.internal;
+import com.google.common.annotations.VisibleForTesting;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.onosproject.config.DynamicConfigService;
+
+import org.onosproject.config.Filter;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.odtn.utils.tapi.DcsBasedTapiNepRef;
+import org.onosproject.odtn.utils.tapi.DcsBasedTapiNodeRef;
+import org.onosproject.odtn.utils.tapi.TapiNepRef;
+import org.onosproject.odtn.utils.tapi.TapiNodeRef;
+import org.onosproject.yang.gen.v1.tapicommon.rev20180307.tapicommon.DefaultContext;
+import org.onosproject.yang.gen.v1.tapicommon.rev20180307.tapicommon.globalclass.Name;
+import org.onosproject.yang.gen.v1.tapitopology.rev20180307.tapitopology.context.DefaultAugmentedTapiCommonContext;
+import org.onosproject.yang.gen.v1.tapitopology.rev20180307.tapitopology.topologycontext.Topology;
+import org.onosproject.yang.model.Augmentable;
+import org.onosproject.yang.model.DataNode;
+import org.onosproject.yang.model.DefaultModelObjectData;
+import org.onosproject.yang.model.DefaultResourceData;
+import org.onosproject.yang.model.ModelConverter;
+import org.onosproject.yang.model.ModelObject;
+import org.onosproject.yang.model.ModelObjectData;
+import org.onosproject.yang.model.ModelObjectId;
+import org.onosproject.yang.model.ResourceData;
+import org.onosproject.yang.model.ResourceId;
+import org.slf4j.Logger;
+
+import static org.onlab.osgi.DefaultServiceDirectory.getService;
+import static org.onosproject.odtn.utils.tapi.TapiInstanceBuilder.DEVICE_ID;
+import static org.onosproject.odtn.utils.tapi.TapiInstanceBuilder.ONOS_CP;
+import static org.slf4j.LoggerFactory.getLogger;
+
+public class DcsBasedTapiDataProducer implements TapiDataProducer {
+
+    private final Logger log = getLogger(getClass());
+    protected DynamicConfigService dcs;
+    protected ModelConverter modelConverter;
+
+    @Override
+    public void init() {
+        dcs = getService(DynamicConfigService.class);
+        modelConverter = getService(ModelConverter.class);
+    }
+
+    @Override
+    public void updateCacheRequest(DefaultTapiResolver resolver) {
+        ModelObject context = readContextModelObject();
+        updateCache(resolver, context);
+    }
+
+    @VisibleForTesting
+    protected void updateCache(DefaultTapiResolver resolver, ModelObject context) {
+        updateNodes(resolver, getNodes(context));
+        updateNeps(resolver, getNeps(context));
+    }
+
+    private ModelObject readContextModelObject() {
+        // read DataNode from DCS
+        ModelObjectId mid = ModelObjectId.builder().addChild(DefaultContext.class).build();
+        DataNode node = dcs.readNode(getResourceId(mid), Filter.builder().build());
+
+        // convert to ModelObject
+        ResourceData data = DefaultResourceData.builder().addDataNode(node)
+                .resourceId(ResourceId.builder().build()).build();
+        ModelObjectData modelData = modelConverter.createModel(data);
+        ModelObject context = modelData.modelObjects().get(0);
+
+        return context;
+    }
+
+    private List<TapiNodeRef> getNodes(ModelObject context) {
+        Augmentable augmentedContext = (Augmentable) context;
+        DefaultAugmentedTapiCommonContext topologyContext
+                = augmentedContext.augmentation(DefaultAugmentedTapiCommonContext.class);
+        Topology topology = topologyContext.topology().get(0);
+
+        if (topology.node() == null) {
+            return Collections.emptyList();
+        }
+        return topology.node().stream()
+                .map(node -> {
+                    String deviceId = node.name().stream()
+                            .filter(kv -> kv.valueName().equals(DEVICE_ID))
+                            .findFirst().map(Name::value).get();
+                    return DcsBasedTapiNodeRef.create(topology, node)
+                            .setDeviceId(DeviceId.deviceId(deviceId));
+                })
+                .collect(Collectors.toList());
+    }
+
+    private List<TapiNepRef> getNeps(ModelObject context) {
+        Augmentable augmentedContext = (Augmentable) context;
+        DefaultAugmentedTapiCommonContext topologyContext
+                = augmentedContext.augmentation(DefaultAugmentedTapiCommonContext.class);
+        Topology topology = topologyContext.topology().get(0);
+
+        if (topology.node() == null) {
+            return Collections.emptyList();
+        }
+        List<TapiNepRef> ret = topology.node().stream()
+                .flatMap(node -> {
+                    if (node.ownedNodeEdgePoint() == null) {
+                        return null;
+                    }
+                    return node.ownedNodeEdgePoint().stream()
+                                    .map(nep -> {
+                                        String onosConnectPoint = nep.name().stream()
+                                                .filter(kv -> kv.valueName().equals(ONOS_CP))
+                                                .findFirst().map(Name::value).get();
+                                        TapiNepRef nepRef = DcsBasedTapiNepRef.create(topology, node, nep)
+                                                .setConnectPoint(ConnectPoint.fromString(onosConnectPoint));
+                                        if (nep.mappedServiceInterfacePoint() != null) {
+                                            nep.mappedServiceInterfacePoint().stream()
+                                                    .forEach(sip -> {
+                                                        nepRef.setSipId(sip.serviceInterfacePointId().toString());
+                                                    });
+                                        }
+                                        return nepRef;
+                                    });
+                        }
+                ).collect(Collectors.toList());
+        return ret;
+    }
+
+    private void updateNodes(DefaultTapiResolver resolver, List<TapiNodeRef> nodes) {
+        resolver.addNodeRefList(nodes);
+    }
+
+    private void updateNeps(DefaultTapiResolver resolver, List<TapiNepRef> neps) {
+        resolver.addNepRefList(neps);
+    }
+
+
+
+    private ResourceId getResourceId(ModelObjectId modelId) {
+        ModelObjectData data = DefaultModelObjectData.builder()
+                .identifier(modelId)
+                .build();
+        ResourceData rnode = modelConverter.createDataNode(data);
+        return rnode.resourceId();
+    }
+
+}
diff --git a/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/DcsBasedTapiTopologyManager.java b/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/DcsBasedTapiTopologyManager.java
index 6da8cbf..1ab7f12 100644
--- a/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/DcsBasedTapiTopologyManager.java
+++ b/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/DcsBasedTapiTopologyManager.java
@@ -16,14 +16,12 @@
 
 package org.onosproject.odtn.internal;
 
-import java.util.List;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
-import org.onlab.util.XmlString;
 import org.onosproject.config.DynamicConfigService;
 import org.onosproject.config.FailedException;
 import org.onosproject.config.Filter;
@@ -38,14 +36,9 @@
 import org.onosproject.net.Link;
 import org.onosproject.net.Port;
 
-import static org.onosproject.odtn.utils.YangToolUtil.toCharSequence;
-import static org.onosproject.odtn.utils.YangToolUtil.toCompositeData;
-import static org.onosproject.odtn.utils.YangToolUtil.toXmlCompositeStream;
-
 import org.onosproject.odtn.utils.tapi.TapiLinkBuilder;
 import org.onosproject.odtn.utils.tapi.TapiNepRef;
 import org.onosproject.odtn.utils.tapi.TapiNodeRef;
-import org.onosproject.odtn.utils.tapi.TapiResolver;
 import org.onosproject.odtn.utils.tapi.TapiContextBuilder;
 import org.onosproject.odtn.utils.tapi.TapiNepBuilder;
 import org.onosproject.odtn.utils.tapi.TapiNodeBuilder;
@@ -57,9 +50,6 @@
 import org.onosproject.yang.model.DataNode;
 import org.onosproject.yang.model.InnerNode;
 import org.onosproject.yang.model.ModelConverter;
-import org.onosproject.yang.model.ModelObjectData;
-import org.onosproject.yang.model.ResourceData;
-import org.onosproject.yang.model.ResourceId;
 import org.slf4j.Logger;
 
 import static org.slf4j.LoggerFactory.getLogger;
@@ -79,11 +69,12 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ModelConverter modelConverter;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected TapiResolver tapiResolver;
+
     private DefaultContext context = new DefaultContext();
     private DefaultTopology topology = new DefaultTopology();
 
-    private TapiResolver tapiResolver = new TapiResolver();
-
     @Activate
     public void activate() {
         initDcsIfRootNotExist();
@@ -104,14 +95,9 @@
         if (tapiResolver.hasNodeRef(deviceId)) {
             return;
         }
-        TapiNodeBuilder builder = TapiNodeBuilder.builder()
+        TapiNodeBuilder.builder()
                 .setTopologyUuid(topology.uuid())
-                .setDeviceId(deviceId);
-        addModelObjectDataToDcs(builder.build());
-
-        TapiNodeRef nodeRef = new TapiNodeRef(topology.uuid().toString(), builder.getUuid().toString());
-        nodeRef.setDeviceId(deviceId);
-        tapiResolver.addNodeRef(nodeRef);
+                .setDeviceId(deviceId).build();
     }
 
     @Override
@@ -123,21 +109,16 @@
     public void addLink(Link link) {
         log.info("Add link: {}", link);
 
-        // validation check
-
-        // src nep
-        addNep(link.src());
-        addNep(link.dst());
+        // TODO: existence check
 
         // link
         TapiNepRef srcNepRef = tapiResolver.getNepRef(link.src());
         TapiNepRef dstNepRef = tapiResolver.getNepRef(link.dst());
 
-        TapiLinkBuilder linkBuilder = TapiLinkBuilder.builder()
+        TapiLinkBuilder.builder()
                 .setTopologyUuid(topology.uuid())
-                .setNep(srcNepRef)
-                .setNep(dstNepRef);
-        addModelObjectDataToDcs(linkBuilder.build());
+                .addNep(srcNepRef)
+                .addNep(dstNepRef).build();
     }
 
     @Override
@@ -148,7 +129,9 @@
     @Override
     public void addPort(Port port) {
         log.info("Add port: {}", port);
-        if (tapiResolver.hasNepRef(new ConnectPoint(port.element().id(), port.number()))) {
+
+        ConnectPoint cp = new ConnectPoint(port.element().id(), port.number());
+        if (tapiResolver.hasNepRef(cp)) {
             return;
         }
 
@@ -157,24 +140,18 @@
 
         // nep
         TapiNepBuilder nepBuilder = TapiNepBuilder.builder()
-                .setPort(port)
+                .setConnectPoint(cp)
                 .setTopologyUuid(topology.uuid())
                 .setNodeUuid(Uuid.fromString(nodeId));
 
-        TapiNepRef nepRef = new TapiNepRef(topology.uuid().toString(), nodeId, nepBuilder.getUuid().toString());
-        nepRef.setConnectPoint(nepBuilder.getConnectPoint());
-
         // sip
-        if (TapiSipBuilder.isSip(port)) {
-            TapiSipBuilder sipBuilder = TapiSipBuilder.builder().setPort(port);
-            nepBuilder.setSip(sipBuilder.getUuid());
-            nepRef.setSipId(sipBuilder.getUuid().toString());
+        if (TapiSipBuilder.isSip(cp)) {
 
-            addModelObjectDataToDcs(sipBuilder.build());
+            TapiSipBuilder sipBuilder = TapiSipBuilder.builder().setConnectPoint(cp);
+            nepBuilder.addSip(sipBuilder.getUuid());
+            sipBuilder.build();
         }
-
-        addModelObjectDataToDcs(nepBuilder.build());
-        tapiResolver.addNepRef(nepRef);
+        nepBuilder.build();
     }
 
     @Override
@@ -182,60 +159,16 @@
         log.info("Remove port: {}", port);
     }
 
-    private void addNep(ConnectPoint cp) {
-
-        log.info("device Id: {}", cp.deviceId());
-        TapiNodeRef nodeRef = tapiResolver.getNodeRef(cp.deviceId());
-        String nodeId = nodeRef.getNodeId();
-
-        TapiNepBuilder nepBuilder = TapiNepBuilder.builder()
-                .setConnectPoint(cp)
-                .setTopologyUuid(topology.uuid())
-                .setNodeUuid(Uuid.fromString(nodeId));
-        TapiNepRef nepRef = new TapiNepRef(topology.uuid().toString(), nodeId, nepBuilder.getUuid().toString());
-        nepRef.setConnectPoint(cp);
-
-        addModelObjectDataToDcs(nepBuilder.build());
-        tapiResolver.addNepRef(nepRef);
-    }
-
     private void initDcsTapiContext() {
-        TapiContextBuilder builder = TapiContextBuilder.builder(context);
-        addModelObjectDataToDcs(builder.build());
+        TapiContextBuilder.builder(context).build();
     }
 
     private void initDcsTapiTopology() {
-        TapiTopologyBuilder builder = TapiTopologyBuilder.builder(topology);
-        addModelObjectDataToDcs(builder.build());
+        TapiTopologyBuilder.builder(topology).build();
     }
 
     // FIXME: move DCS-related methods to DCS
 
-    private void addModelObjectDataToDcs(ModelObjectData input) {
-
-        ResourceData rnode = modelConverter.createDataNode(input);
-
-        // for debug
-        CharSequence strNode = toCharSequence(toXmlCompositeStream(toCompositeData(rnode)));
-        log.info("XML:\n{}", XmlString.prettifyXml(strNode));
-
-        addResourceDataToDcs(rnode);
-    }
-
-    private void addResourceDataToDcs(ResourceData input) {
-        addResourceDataToDcs(input, input.resourceId());
-    }
-
-    private void addResourceDataToDcs(ResourceData input, ResourceId rid) {
-        if (input == null || input.dataNodes() == null) {
-            return;
-        }
-        List<DataNode> dataNodes = input.dataNodes();
-        for (DataNode node : dataNodes) {
-            dcs.createNode(rid, node);
-        }
-    }
-
     private void initDcsIfRootNotExist() {
 
         log.info("read root:");
diff --git a/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/DefaultTapiResolver.java b/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/DefaultTapiResolver.java
new file mode 100644
index 0000000..f5a9bc6
--- /dev/null
+++ b/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/DefaultTapiResolver.java
@@ -0,0 +1,195 @@
+/*
+ * 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.odtn.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.CopyOnWriteArrayList;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.ElementId;
+import org.onosproject.odtn.utils.tapi.TapiNepRef;
+import org.onosproject.odtn.utils.tapi.TapiNodeRef;
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+
+@Component(immediate = true)
+@Service
+public class DefaultTapiResolver implements TapiResolver {
+
+    private final Logger log = getLogger(getClass());
+
+    protected TapiDataProducer dataProvider = new DcsBasedTapiDataProducer();
+
+    private List<TapiNodeRef> tapiNodeRefList = new CopyOnWriteArrayList<>();
+    private List<TapiNepRef> tapiNepRefList = new CopyOnWriteArrayList<>();
+
+    /**
+     * When source (e.g. DCS) is updated, set true
+     * When cache update completed successfully, set false
+     *
+     * This flag takes effect when cache update failed with exception,
+     * this remains to be true so the cache update process conducts again
+     */
+    private Boolean isDirty = false;
+
+    /**
+     * When source (e.g. DCS) is updated, set true
+     * When cache update started, set false
+     *
+     * This flag takes effect when source updated during cache updating
+     * this forces cache update again at the next request
+     */
+    private Boolean sourceUpdated = false;
+
+    @Activate
+    public void activate() {
+        log.info("Started");
+        dataProvider.init();
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public boolean hasNodeRef(ElementId deviceId) {
+        updateCache();
+        return tapiNodeRefList.stream()
+                .anyMatch(node -> node.getDeviceId().equals(deviceId));
+    }
+
+    @Override
+    public boolean hasNepRef(ConnectPoint cp) {
+        updateCache();
+        return tapiNepRefList.stream()
+                .anyMatch(nep -> nep.getConnectPoint().equals(cp));
+    }
+
+    @Override
+    public boolean hasNepRef(String sipId) {
+        updateCache();
+        return tapiNepRefList.stream()
+                .anyMatch(nep -> nep.getSipId().equals(sipId));
+    }
+
+    @Override
+    public TapiNodeRef getNodeRef(ElementId deviceId) throws NoSuchElementException {
+        updateCache();
+        TapiNodeRef ret = null;
+        try {
+            ret = tapiNodeRefList.stream()
+                    .filter(node -> node.getDeviceId().equals(deviceId))
+                    .findFirst().get();
+        } catch (NoSuchElementException e) {
+            log.error("Node not found associated with {}", deviceId);
+            throw e;
+        }
+        return ret;
+    }
+
+    @Override
+    public List<TapiNodeRef> getNodeRefs() {
+        updateCache();
+        return new ArrayList<>(tapiNodeRefList);
+    }
+
+    @Override
+    public TapiNepRef getNepRef(ConnectPoint cp) throws NoSuchElementException {
+        updateCache();
+        TapiNepRef ret = null;
+        try {
+            ret = tapiNepRefList.stream()
+                    .filter(nep -> nep.getConnectPoint().equals(cp))
+                    .findFirst().get();
+        } catch (NoSuchElementException e) {
+            log.error("Nep not found associated with {}", cp);
+            throw e;
+        }
+        return ret;
+    }
+
+    @Override
+    public TapiNepRef getNepRef(String sipId) throws NoSuchElementException {
+        updateCache();
+        TapiNepRef ret = null;
+        try {
+            ret = tapiNepRefList.stream()
+                    .filter(nep -> nep.getSipId().equals(sipId))
+                    .findFirst().get();
+        } catch (NoSuchElementException e) {
+            log.error("Nep not found associated with {}", sipId);
+            throw e;
+        }
+        return ret;
+    }
+
+    @Override
+    public List<TapiNepRef> getNepRefs() {
+        updateCache();
+        return new ArrayList<>(tapiNepRefList);
+    }
+
+    @Override
+    public void makeDirty() {
+        sourceUpdated = true;
+        isDirty = true;
+    }
+
+    protected void addNodeRef(TapiNodeRef nodeRef) {
+        tapiNodeRefList.add(nodeRef);
+        log.info("Nodes: {}", tapiNodeRefList);
+    }
+
+    protected void addNepRef(TapiNepRef nepRef) {
+        tapiNepRefList.add(nepRef);
+        log.info("Neps: {}", tapiNepRefList);
+    }
+
+    protected void addNodeRefList(List<TapiNodeRef> nodes) {
+        tapiNodeRefList = nodes;
+        log.info("Nodes: {}", tapiNodeRefList);
+    }
+
+    protected void addNepRefList(List<TapiNepRef> neps) {
+        tapiNepRefList = neps;
+        log.info("Neps: {}", tapiNepRefList);
+    }
+
+    private void updateCache() {
+        log.info("Dirty: {}, Source updated: {}", isDirty, sourceUpdated);
+        if (isDirty || sourceUpdated) {
+            sourceUpdated = false;
+            clearCache();
+            dataProvider.updateCacheRequest(this);
+            log.info("Update completed: {}", tapiNodeRefList);
+            isDirty = false;
+        }
+    }
+
+    private void clearCache() {
+        tapiNodeRefList.clear();
+        tapiNepRefList.clear();
+    }
+}
diff --git a/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/TapiDataProducer.java b/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/TapiDataProducer.java
new file mode 100644
index 0000000..8d0c2d5
--- /dev/null
+++ b/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/TapiDataProducer.java
@@ -0,0 +1,32 @@
+/*
+ * 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.odtn.internal;
+
+/**
+ * ODTN Tapi data reader/provider for TapiResolver cache update.
+ */
+public interface TapiDataProducer {
+
+    void init();
+
+    /**
+     * Update TapiResolver cache with latest modelObject in DCS.
+     * @param resolver update target itself
+     */
+    void updateCacheRequest(DefaultTapiResolver resolver);
+
+}
diff --git a/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/TapiResolver.java b/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/TapiResolver.java
new file mode 100644
index 0000000..5311cd9
--- /dev/null
+++ b/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/TapiResolver.java
@@ -0,0 +1,111 @@
+/*
+ * 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.odtn.internal;
+
+import java.util.List;
+import java.util.NoSuchElementException;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.ElementId;
+import org.onosproject.odtn.utils.tapi.TapiNepRef;
+import org.onosproject.odtn.utils.tapi.TapiNodeRef;
+
+/**
+ * TAPI Yang object resolver.
+ * <p>
+ * This service works as TAPI Yang object cache, and provide
+ * TAPI object resolve service, e.g. TAPI Node/NodeEdgePoint/Link.
+ * This is independent with DCS and uses not DCS ModelObject directly
+ * but DCS-independent object like TapiNodeRef/TapiNepRef.
+ * This should work together with TapiDataProducer, which acts as
+ * data producer by reading TAPI modelObjects from DCS and convert them
+ * to DCS-independent objects.
+ */
+public interface TapiResolver {
+
+    /**
+     * Check existence of TAPI node associated with deviceId.
+     *
+     * @param deviceId search key
+     * @return boolean
+     */
+    boolean hasNodeRef(ElementId deviceId);
+
+    /**
+     * Check existence of TAPI nep associated with ConnectPoint.
+     *
+     * @param cp search key
+     * @return TapiNepRef
+     */
+    boolean hasNepRef(ConnectPoint cp);
+
+    /**
+     * Check existence of TAPI nep associated with TAPI sipId.
+     *
+     * @param sipId search key
+     * @return TapiNepRef
+     */
+    boolean hasNepRef(String sipId);
+
+    /**
+     * Resolve TAPI node associated with deviceId.
+     *
+     * @param deviceId search key
+     * @return TapiNodeRef
+     * @throws NoSuchElementException if target not found
+     */
+    TapiNodeRef getNodeRef(ElementId deviceId);
+
+    /**
+     * Get all NodeRefs.
+     *
+     * @return List&lt;TapiNodeRef&gt;
+     */
+    List<TapiNodeRef> getNodeRefs();
+
+    /**
+     * Resolve TAPI nep associated with ConnectPoint.
+     *
+     * @param cp search key
+     * @return TapiNepRef
+     * @throws NoSuchElementException if target not found
+     */
+    TapiNepRef getNepRef(ConnectPoint cp);
+
+    /**
+     * Resolve TAPI nep associated with TAPI sipId.
+     *
+     * @param sipId search key
+     * @return TapiNepRef
+     * @throws NoSuchElementException if target not found
+     */
+    TapiNepRef getNepRef(String sipId);
+
+    /**
+     * Get all NepRefs.
+     *
+     * @return List&lt;TapiNepRef&gt;
+     */
+    List<TapiNepRef> getNepRefs();
+
+    /**
+     * Inform the cache is already got dirty and let it update cache.
+     * The cache update process is conducted when next resolve request
+     * (hasXXX or getXXX) comes.
+     */
+    void makeDirty();
+
+}
diff --git a/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/TapiTopologyManager.java b/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/TapiTopologyManager.java
index 0f02c63..1be4ed5 100644
--- a/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/TapiTopologyManager.java
+++ b/apps/odtn/service/src/main/java/org/onosproject/odtn/internal/TapiTopologyManager.java
@@ -21,7 +21,7 @@
 import org.onosproject.net.Port;
 
 /**
- * ODTN Tapi manager application interface.
+ * ODTN Tapi topology manager application interface.
  */
 public interface TapiTopologyManager {