blob: 09cc1e0c6e8c13989c436201f340d59c5874ea43 [file] [log] [blame]
/*
* Copyright 2017-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.yang.runtime.impl;
import org.onosproject.yang.compiler.datamodel.SchemaDataNode;
import org.onosproject.yang.compiler.datamodel.YangChoice;
import org.onosproject.yang.compiler.datamodel.YangInclude;
import org.onosproject.yang.compiler.datamodel.YangModule;
import org.onosproject.yang.compiler.datamodel.YangNode;
import org.onosproject.yang.compiler.datamodel.YangSchemaNode;
import org.onosproject.yang.compiler.datamodel.YangSchemaNodeIdentifier;
import org.onosproject.yang.compiler.datamodel.YangSubModule;
import org.onosproject.yang.compiler.datamodel.exceptions.DataModelException;
import org.onosproject.yang.compiler.tool.YangModuleExtendedInfo;
import org.onosproject.yang.model.DataNode;
import org.onosproject.yang.model.ModelConverterException;
import org.onosproject.yang.model.ModelObjectId;
import org.onosproject.yang.model.SchemaContext;
import org.onosproject.yang.model.SchemaId;
import org.onosproject.yang.model.SingleInstanceNodeContext;
import org.onosproject.yang.model.YangModel;
import org.onosproject.yang.model.YangModuleId;
import org.onosproject.yang.runtime.AppModuleInfo;
import org.onosproject.yang.runtime.ModelRegistrationParam;
import org.onosproject.yang.runtime.YangModelRegistry;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.Collections.sort;
import static java.util.Collections.unmodifiableSet;
import static org.onosproject.yang.compiler.datamodel.YangSchemaNodeType.YANG_ANYDATA_NODE;
import static org.onosproject.yang.compiler.datamodel.utils.DataModelUtils.getDateInStringFormat;
import static org.onosproject.yang.compiler.datamodel.utils.DataModelUtils.getNodeIdFromSchemaId;
import static org.onosproject.yang.compiler.translator.tojava.JavaCodeGeneratorUtil.updateTreeContext;
import static org.onosproject.yang.compiler.utils.UtilConstants.REGEX;
import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_NODE;
import static org.onosproject.yang.runtime.RuntimeHelper.getInterfaceClassName;
import static org.onosproject.yang.runtime.RuntimeHelper.getNodes;
import static org.onosproject.yang.runtime.RuntimeHelper.getSelfNodes;
import static org.onosproject.yang.runtime.RuntimeHelper.getServiceName;
import static org.onosproject.yang.runtime.impl.UtilsConstants.AT;
import static org.onosproject.yang.runtime.impl.UtilsConstants.E_NEXIST;
import static org.onosproject.yang.runtime.impl.UtilsConstants.E_NOT_VAL;
import static org.onosproject.yang.runtime.impl.UtilsConstants.E_NULL;
import static org.onosproject.yang.runtime.impl.UtilsConstants.FMT_INV;
import static org.onosproject.yang.runtime.impl.UtilsConstants.errorMsg;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Represents YANG model registry implementation.
*/
public class DefaultYangModelRegistry implements YangModelRegistry,
SingleInstanceNodeContext {
private final Logger log = getLogger(getClass());
/*
* Map for storing YANG schema nodes. Key will be the schema name of
* module node defined in YANG file.
*/
private final ConcurrentMap<String, ConcurrentMap<String, YangSchemaNode>>
yangSchemaStore;
/*
* Map for storing YANG schema nodes with respect to root's generated
* file name by which registration is being done.
*/
private final ConcurrentMap<String, YangSchemaNode> regClassNameKeyStore;
/*
* Map for storing YANG schema nodes with respect to root's generated
* file's qualified name.
*/
private final ConcurrentMap<String, YangSchemaNode> qNameKeyStore;
/*
* Map for storing registered classes. Will be used by YOB for class
* loading operations. key will be qualified name of generated class.
* It will be maintained for top level class i.e. module.
*/
private final ConcurrentMap<String, Class<?>> registerClassStore;
/**
* Map for storing schema nodes with respect to namespace. Will be used
* by YCH in scenario where module name is not present in XML.
*/
private final ConcurrentMap<String, YangSchemaNode> nameSpaceSchemaStore;
/**
* Map for storing YANG model with respect to model identifier. Will
* be used by other application to register and unregister the YANG
* models based on model identifier.
*/
private final ConcurrentMap<String, YangModel> modelIdStore;
/**
* Creates an instance of default YANG schema registry.
*/
public DefaultYangModelRegistry() {
yangSchemaStore = new ConcurrentHashMap<>();
regClassNameKeyStore = new ConcurrentHashMap<>();
registerClassStore = new ConcurrentHashMap<>();
nameSpaceSchemaStore = new ConcurrentHashMap<>();
qNameKeyStore = new ConcurrentHashMap<>();
modelIdStore = new ConcurrentHashMap<>();
}
@Override
public void registerModel(ModelRegistrationParam param) throws
IllegalArgumentException {
YangModel model = checkNotNull(param.getYangModel(), E_NULL);
Set<YangNode> curNodes = getNodes(model, yangSchemaStore);
//adding class info if added by application.
AppModuleInfo info = null;
for (YangModuleId id : param.getYangModel().getYangModulesId()) {
info = param.getAppModuleInfo(id);
if (info != null) {
break;
}
}
// Validating the model Id
String id = model.getYangModelId();
if (!id.matches(REGEX)) {
throw new IllegalArgumentException(E_NOT_VAL);
}
if (!modelIdStore.containsKey(id)) {
updateRegClassStore(param);
modelIdStore.put(id, model);
} else if ((info != null) &&
(!registerClassStore.containsValue(info.getModuleClass()))) {
updateRegClassStore(param);
} else {
throw new IllegalArgumentException("ModelId " + id + " already exist");
}
//Register all the YANG nodes, excluding nodes from dependent jar.
if (curNodes != null && !curNodes.isEmpty()) {
for (YangNode node : curNodes) {
registerModule(node);
}
}
//update child context
updateChildContext(curNodes);
log.info("ModelId: {} registered!", id);
}
@Override
public void registerAnydataSchema(ModelObjectId aid, ModelObjectId cid) throws
IllegalArgumentException {
YangSchemaNode anySchema = null;
try {
ModIdToRscIdConverter conv = new ModIdToRscIdConverter(this);
anySchema = ((YangSchemaNode) conv.fetchResourceId(aid).appInfo());
if (anySchema != null &&
anySchema.getYangSchemaNodeType() == YANG_ANYDATA_NODE) {
YangSchemaNode cSchema = ((YangSchemaNode) conv
.fetchResourceId(cid).appInfo());
if (cSchema != null) {
YangSchemaNode clonedNode = anySchema.addSchema(cSchema);
updateTreeContext(clonedNode, null, false, false);
} else {
throw new IllegalArgumentException(errorMsg(FMT_INV, cid));
}
} else {
throw new IllegalArgumentException(errorMsg(FMT_INV, aid));
}
} catch (ModelConverterException e) {
ModelObjectId id = cid;
if (anySchema == null) {
id = aid;
}
throw new IllegalArgumentException(errorMsg(FMT_INV, id));
}
}
@Override
public void unregisterAnydataSchema(Class id, Class id1) throws
IllegalArgumentException {
//TODO implemention
}
/**
* Register specific model.
*
* @param node YANG node
*/
private void registerModule(YangNode node) {
String name;
//register all the nodes present in YANG model.
name = getInterfaceClassName(node);
processApplicationContext(node, name);
}
@Override
public void unregisterModel(ModelRegistrationParam param) {
synchronized (DefaultYangModelRegistry.class) {
YangModel model = checkNotNull(param.getYangModel(), E_NULL);
modelIdStore.remove(model.getYangModelId());
//Unregister all yang files, excluding nodes from dependent jar.
Set<YangNode> curNodes = getSelfNodes(model, yangSchemaStore);
if (curNodes != null && !curNodes.isEmpty()) {
for (YangNode node : curNodes) {
processUnReg(getInterfaceClassName(node));
}
}
}
}
private void processUnReg(String serviceName) {
YangSchemaNode curNode = regClassNameKeyStore.get(serviceName);
if (curNode != null) {
removeSchemaNode(curNode);
regClassNameKeyStore.remove(serviceName);
qNameKeyStore.remove(serviceName.toLowerCase());
nameSpaceSchemaStore.remove(
curNode.getNameSpace().getModuleNamespace());
registerClassStore.remove(serviceName);
log.info(" service class {} of model is " +
"unregistered.", serviceName);
} else {
log.error("Either {} service was not registered or " +
"already unregistered from model " +
"registry.", serviceName);
}
}
@Override
public Set<YangModel> getModels() {
Set<YangModel> models = new LinkedHashSet<>();
for (Map.Entry<String, YangModel> entry : modelIdStore.entrySet()) {
models.add(entry.getValue());
}
return unmodifiableSet(models);
}
/**
* Returns schema node for the given name. Name should be schema defined
* name.
*
* @param schemaName schema name
* @return YANG schema node
*/
public YangSchemaNode getForSchemaName(String schemaName) {
return getForNameWithRev(schemaName);
}
@Override
public YangModel getModel(String id) {
return modelIdStore.get(id);
}
@Override
public org.onosproject.yang.model.YangModule getModule(YangModuleId id) {
for (Map.Entry<String, YangModel> entry : modelIdStore
.entrySet()) {
org.onosproject.yang.model.YangModule module = entry.getValue()
.getYangModule(id);
if (module != null) {
return module;
}
}
return null;
}
/**
* Returns schema node for the given name. Name should be generated class
* name. the name provided here should be for registered class.
*
* @param name interface class name
* @return YANG schema node
*/
YangSchemaNode getForRegClassName(String name) {
YangSchemaNode node = regClassNameKeyStore.get(name);
if (node == null) {
log.error("{} not found.", name);
}
return node;
}
/**
* Returns schema node for the given package. pkg should be generated class
* pkg. the pkg provided here should be for registered interface class.
* pkg string contains the java package and java name of the module
* generated class in lower case.
*
* @param pkg interface class pkg
* @param isFromDnb true when request has come from data tree builder
* @return YANG schema node
*/
YangSchemaNode getForRegClassQualifiedName(String pkg, boolean isFromDnb) {
YangSchemaNode node = qNameKeyStore.get(pkg);
if (node == null && !isFromDnb) {
log.error("{} not found.", pkg);
}
return node;
}
/**
* Returns schema node for the given name. Name should be nodes namespace
* defined in YANG file
*
* @param nameSpace name space of YANG file
* @param isForChildContext if the method call has arrived for child context
* @return YANG schema node
*/
public YangSchemaNode getForNameSpace(String nameSpace,
boolean isForChildContext) {
YangSchemaNode node = nameSpaceSchemaStore.get(nameSpace);
if (node == null && !isForChildContext) {
log.error(E_NEXIST, nameSpace);
}
return node;
}
/**
* Returns registered service for given schema node.
*
* @param schemaNode schema node
* @return registered class
*/
Class<?> getRegisteredClass(YangSchemaNode schemaNode) {
Class<?> regClass = null;
if (schemaNode != null) {
String interfaceName = getInterfaceClassName(schemaNode);
String serviceName = getServiceName(schemaNode);
regClass = registerClassStore.get(serviceName);
if (regClass == null) {
regClass = registerClassStore.get(interfaceName);
}
if (regClass == null) {
log.error("Nothing registered for {} or {}", serviceName, interfaceName);
}
}
return regClass;
}
/**
* Process an application an updates the maps for YANG model registry.
*
* @param appNode application YANG schema nodes
* @param name class name
*/
void processApplicationContext(YangSchemaNode appNode, String name) {
// Updates schema store.
addToSchemaStore(appNode);
// update interface store.
regClassNameKeyStore.put(name, appNode);
qNameKeyStore.put(getInterfaceClassName(appNode).toLowerCase(), appNode);
/*
* The name of a module determines the namespace of all data node names
* defined in that module. If a data node is defined in a submodule,
* then the namespace-qualified member name uses the name of the main
* module to which the submodule belongs.
* So skipping the submodule entry in namespace map.
*/
if (!(appNode instanceof YangSubModule)) {
//update namespaceSchema store.
nameSpaceSchemaStore.put(appNode.getNameSpace().getModuleNamespace(),
appNode);
}
log.info("successfully registered this application {}", name);
}
/**
* Returns schema node based on the revision.
*
* @param name name of the schema node
* @return schema node based on the revision
*/
private YangSchemaNode getForNameWithRev(String name) {
ConcurrentMap<String, YangSchemaNode> revMap;
YangSchemaNode schemaNode;
if (name.contains(AT)) {
String[] revArray = name.split(AT);
revMap = yangSchemaStore.get(revArray[0]);
schemaNode = revMap.get(name);
if (schemaNode == null) {
log.error("{} not found.", name);
}
return schemaNode;
}
if (yangSchemaStore.containsKey(name)) {
revMap = yangSchemaStore.get(name);
if (revMap != null && !revMap.isEmpty()) {
YangSchemaNode node = revMap.get(name);
if (node != null) {
return node;
}
String revName = getLatestVersion(revMap);
return revMap.get(revName);
}
}
log.error("{} not found.", name);
return null;
}
private String getLatestVersion(ConcurrentMap<String,
YangSchemaNode> revMap) {
List<String> keys = new ArrayList<>();
for (Map.Entry<String, YangSchemaNode> entry : revMap.entrySet()) {
keys.add(entry.getKey());
}
sort(keys);
return keys.get(keys.size() - 1);
}
/**
* Adds schema node when different revision of node has received.
*
* @param schemaNode schema node
*/
private void addToSchemaStore(YangSchemaNode schemaNode) {
String date = getDateInStringFormat((YangNode) schemaNode);
String name = schemaNode.getName();
String revName = name;
if (date != null) {
revName = name + AT + date;
}
//check if already present.
if (!yangSchemaStore.containsKey(name)) {
ConcurrentMap<String, YangSchemaNode> revStore =
new ConcurrentHashMap<>();
revStore.put(revName, schemaNode);
yangSchemaStore.put(name, revStore);
} else {
yangSchemaStore.get(name).put(revName, schemaNode);
}
}
/**
* Removes schema node from schema map.
*
* @param removableNode schema node which needs to be removed
*/
private void removeSchemaNode(YangSchemaNode removableNode) {
String name = removableNode.getName();
String revName = name;
String date = getDateInStringFormat((YangNode) removableNode);
if (date != null) {
revName = name + AT + date;
}
ConcurrentMap<String, YangSchemaNode> revMap =
yangSchemaStore.get(name);
if (revMap != null && !revMap.isEmpty() && revMap.size() != 1) {
revMap.remove(revName);
} else {
yangSchemaStore.remove(removableNode.getName());
}
}
@Override
public SchemaContext getParentContext() {
return null;
}
@Override
public DataNode.Type getType() {
return SINGLE_INSTANCE_NODE;
}
@Override
public SchemaId getSchemaId() {
return new SchemaId("/", null);
}
@Override
public SchemaContext getChildContext(SchemaId schemaId) {
checkNotNull(schemaId);
String ns = schemaId.namespace();
if (ns == null) {
log.error("namespace should not be null for a node");
}
YangSchemaNode schemaNode = null;
YangSchemaNode node = getForNameSpace(ns, true);
if (node == null) {
//If namespace is module name.
node = getForSchemaName(ns);
}
YangSchemaNodeIdentifier id = getNodeIdFromSchemaId(schemaId, ns);
if (node != null) {
try {
schemaNode = node.getChildSchema(id).getSchemaNode();
} catch (DataModelException e) {
// if exception occurs check for submodule
}
if (schemaNode == null) {
List<YangInclude> includeList = ((YangModule) node)
.getIncludeList();
// Checking requested node in submodule.
schemaNode = getSubModlueChildNode(id, includeList);
}
return schemaNode;
} else {
log.error(E_NEXIST, ns);
}
return null;
}
/**
* Updates registered class store.
*
* @param param model registrations param
*/
void updateRegClassStore(ModelRegistrationParam param) {
Class<?> service;
AppModuleInfo info;
for (YangModuleId id : param.getYangModel().getYangModulesId()) {
YangModuleExtendedInfo i = (YangModuleExtendedInfo) param
.getYangModel().getYangModule(id);
if (!i.isInterJar()) {
info = param.getAppModuleInfo(id);
if (info != null) {
service = info.getModuleClass();
addRegClass(service.getName(), service);
}
}
}
}
/**
* Adds the registered class.
*
* @param name qualified name of the class
* @param service generated class
*/
void addRegClass(String name, Class<?> service) {
if (!registerClassStore.containsKey(name)) {
registerClassStore.put(name, service);
}
}
/**
* Updates child's context. It sets itself as a parent context for first
* level child's in module/sub-module.
*
* @param nodes set of module/submodule nodes
*/
private void updateChildContext(Set<YangNode> nodes) {
// Preparing schema id for logical node with name "/"
for (YangNode node : nodes) {
node.setLeafRootContext(this);
YangNode child = node.getChild();
while (child != null) {
if (child instanceof YangChoice) {
updateSchemaContextForChoiceChild(child);
} else if (child instanceof SchemaDataNode) {
child.setRootContext(this);
}
child = child.getNextSibling();
}
}
}
/**
* Updates the parent context for given choice-case node child's.
*
* @param child yang node
*/
private void updateContextForChoiceCase(YangNode child) {
while (child != null) {
if (child instanceof YangChoice) {
updateSchemaContextForChoiceChild(child);
} else if (child instanceof SchemaDataNode) {
child.setRootContext(this);
}
child = child.getNextSibling();
}
}
/**
* Updates the parent context for given choice node child's.
*
* @param curNode choice node
*/
private void updateSchemaContextForChoiceChild(YangNode curNode) {
YangNode child = curNode.getChild();
// Setting the parent context for case
while (child != null) {
updateSchemaContextForCaseChild(child);
child = child.getNextSibling();
}
}
/**
* Updates the parent context for given case node child's.
*
* @param curNode case node
*/
private void updateSchemaContextForCaseChild(YangNode curNode) {
curNode.setLeafRootContext(this);
YangNode child = curNode.getChild();
updateContextForChoiceCase(child);
}
/**
* Returns the child schema for given node id from the list of included
* submodule.
* <p>
* To reference an item that is defined in one of its
* submodules, the module MUST include the submodule and if
* a submodule that needs to reference an item defined in another
* submodule of the same module, MUST include this submodule.
* <p>
* In other words if submodule is including any submodule which
* belongs to same module then module should also include that
* submodule.
*
* @param id child node identifier
* @param list list of included submodule
*/
private YangSchemaNode getSubModlueChildNode(YangSchemaNodeIdentifier id,
List<YangInclude> list) {
YangSchemaNode schemaNode = null;
for (YangInclude l : list) {
try {
schemaNode = l.getIncludedNode().getChildSchema(id)
.getSchemaNode();
} catch (DataModelException e) {
log.error("failed to get child schema", e);
}
}
return schemaNode;
}
}