Java tutorial
/* * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.controller.sal.dom.broker.impl; import static com.google.common.base.Preconditions.checkState; import com.google.common.base.Predicate; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.Future; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.DataModification; import org.opendaylight.controller.md.sal.common.api.data.DataReader; import org.opendaylight.controller.md.sal.common.impl.AbstractDataModification; import org.opendaylight.controller.md.sal.common.impl.util.AbstractLockableDelegator; import org.opendaylight.controller.sal.core.api.data.DataStore; import org.opendaylight.controller.sal.dom.broker.util.YangDataOperations; import org.opendaylight.controller.sal.dom.broker.util.YangSchemaUtils; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.api.SimpleNode; import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataStore> implements // DataStore, // SchemaContextListener, // AutoCloseable { private final static Logger LOG = LoggerFactory.getLogger(SchemaAwareDataStoreAdapter.class); private SchemaContext schema = null; private boolean validationEnabled = false; private final DataReader<YangInstanceIdentifier, CompositeNode> reader = new MergeFirstLevelReader(); @Override public boolean containsConfigurationPath(final YangInstanceIdentifier path) { try { getDelegateReadLock().lock(); return getDelegate().containsConfigurationPath(path); } finally { getDelegateReadLock().unlock(); } } @Override public boolean containsOperationalPath(final YangInstanceIdentifier path) { try { getDelegateReadLock().lock(); return getDelegate().containsOperationalPath(path); } finally { getDelegateReadLock().unlock(); } } @Override public Iterable<YangInstanceIdentifier> getStoredConfigurationPaths() { try { getDelegateReadLock().lock(); return getDelegate().getStoredConfigurationPaths(); } finally { getDelegateReadLock().unlock(); } } @Override public Iterable<YangInstanceIdentifier> getStoredOperationalPaths() { try { getDelegateReadLock().lock(); return getDelegate().getStoredOperationalPaths(); } finally { getDelegateReadLock().unlock(); } } @Override public CompositeNode readConfigurationData(final YangInstanceIdentifier path) { return reader.readConfigurationData(path); } @Override public CompositeNode readOperationalData(final YangInstanceIdentifier path) { return reader.readOperationalData(path); } @Override public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<YangInstanceIdentifier, CompositeNode> requestCommit( final DataModification<YangInstanceIdentifier, CompositeNode> modification) { validateAgainstSchema(modification); NormalizedDataModification cleanedUp = prepareMergedTransaction(modification); cleanedUp.status = TransactionStatus.SUBMITED; return retrieveDelegate().requestCommit(cleanedUp); } public boolean isValidationEnabled() { return validationEnabled; } public void setValidationEnabled(final boolean validationEnabled) { this.validationEnabled = validationEnabled; } private void validateAgainstSchema(final DataModification<YangInstanceIdentifier, CompositeNode> modification) { if (!validationEnabled) { return; } if (schema == null) { LOG.warn("Validation not performed for {}. Reason: YANG Schema not present.", modification.getIdentifier()); return; } } @Override protected void onDelegateChanged(final DataStore oldDelegate, final DataStore newDelegate) { // NOOP } @Override public void onGlobalContextUpdated(final SchemaContext context) { this.schema = context; } @Override public void close() throws Exception { this.schema = null; } protected CompositeNode mergeData(final YangInstanceIdentifier path, final CompositeNode stored, final CompositeNode modified, final boolean config) { // long startTime = System.nanoTime(); try { DataSchemaNode node = schemaNodeFor(path); return YangDataOperations.merge(node, stored, modified, config); } finally { // System.out.println("Merge time: " + ((System.nanoTime() - // startTime) / 1000.0d)); } } private DataSchemaNode schemaNodeFor(final YangInstanceIdentifier path) { checkState(schema != null, "YANG Schema is not available"); return YangSchemaUtils.getSchemaNode(schema, path); } private NormalizedDataModification prepareMergedTransaction( final DataModification<YangInstanceIdentifier, CompositeNode> original) { NormalizedDataModification normalized = new NormalizedDataModification(original); LOG.trace("Transaction: {} Removed Configuration {}, Removed Operational {}", original.getIdentifier(), original.getRemovedConfigurationData(), original.getRemovedConfigurationData()); LOG.trace("Transaction: {} Created Configuration {}, Created Operational {}", original.getIdentifier(), original.getCreatedConfigurationData().entrySet(), original.getCreatedOperationalData().entrySet()); LOG.trace("Transaction: {} Updated Configuration {}, Updated Operational {}", original.getIdentifier(), original.getUpdatedConfigurationData().entrySet(), original.getUpdatedOperationalData().entrySet()); for (YangInstanceIdentifier entry : original.getRemovedConfigurationData()) { normalized.deepRemoveConfigurationData(entry); } for (YangInstanceIdentifier entry : original.getRemovedOperationalData()) { normalized.deepRemoveOperationalData(entry); } for (Entry<YangInstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData() .entrySet()) { normalized.putDeepConfigurationData(entry.getKey(), entry.getValue()); } for (Entry<YangInstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) { normalized.putDeepOperationalData(entry.getKey(), entry.getValue()); } return normalized; } private Iterable<YangInstanceIdentifier> getConfigurationSubpaths(final YangInstanceIdentifier entry) { // FIXME: This should be replaced by index Iterable<YangInstanceIdentifier> paths = getStoredConfigurationPaths(); return getChildrenPaths(entry, paths); } public Iterable<YangInstanceIdentifier> getOperationalSubpaths(final YangInstanceIdentifier entry) { // FIXME: This should be indexed Iterable<YangInstanceIdentifier> paths = getStoredOperationalPaths(); return getChildrenPaths(entry, paths); } private static final Iterable<YangInstanceIdentifier> getChildrenPaths(final YangInstanceIdentifier entry, final Iterable<YangInstanceIdentifier> paths) { ImmutableSet.Builder<YangInstanceIdentifier> children = ImmutableSet.builder(); for (YangInstanceIdentifier potential : paths) { if (entry.contains(potential)) { children.add(entry); } } return children.build(); } private final Comparator<Entry<YangInstanceIdentifier, CompositeNode>> preparationComparator = new Comparator<Entry<YangInstanceIdentifier, CompositeNode>>() { @Override public int compare(final Entry<YangInstanceIdentifier, CompositeNode> o1, final Entry<YangInstanceIdentifier, CompositeNode> o2) { YangInstanceIdentifier o1Key = o1.getKey(); YangInstanceIdentifier o2Key = o2.getKey(); return Integer.compare(o1Key.getPath().size(), o2Key.getPath().size()); } }; private class MergeFirstLevelReader implements DataReader<YangInstanceIdentifier, CompositeNode> { @Override public CompositeNode readConfigurationData(final YangInstanceIdentifier path) { getDelegateReadLock().lock(); try { if (Iterables.isEmpty(path.getPathArguments())) { return null; } QName qname = null; CompositeNode original = getDelegate().readConfigurationData(path); ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>(); if (original != null) { childNodes.addAll(original.getValue()); qname = original.getNodeType(); } else { qname = path.getPath().get(path.getPath().size() - 1).getNodeType(); } FluentIterable<YangInstanceIdentifier> directChildren = FluentIterable .from(getStoredConfigurationPaths()).filter(new Predicate<YangInstanceIdentifier>() { @Override public boolean apply(final YangInstanceIdentifier input) { if (path.contains(input)) { int nesting = input.getPath().size() - path.getPath().size(); if (nesting == 1) { return true; } } return false; } }); for (YangInstanceIdentifier instanceIdentifier : directChildren) { childNodes.add(getDelegate().readConfigurationData(instanceIdentifier)); } if (original == null && childNodes.isEmpty()) { return null; } return new CompositeNodeTOImpl(qname, null, childNodes); } finally { getDelegateReadLock().unlock(); } } @Override public CompositeNode readOperationalData(final YangInstanceIdentifier path) { getDelegateReadLock().lock(); try { if (Iterables.isEmpty(path.getPathArguments())) { return null; } QName qname = null; CompositeNode original = getDelegate().readOperationalData(path); ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>(); if (original != null) { childNodes.addAll(original.getValue()); qname = original.getNodeType(); } else { qname = path.getPath().get(path.getPath().size() - 1).getNodeType(); } FluentIterable<YangInstanceIdentifier> directChildren = FluentIterable .from(getStoredOperationalPaths()).filter(new Predicate<YangInstanceIdentifier>() { @Override public boolean apply(final YangInstanceIdentifier input) { if (path.contains(input)) { int nesting = input.getPath().size() - path.getPath().size(); if (nesting == 1) { return true; } } return false; } }); for (YangInstanceIdentifier instanceIdentifier : directChildren) { childNodes.add(getDelegate().readOperationalData(instanceIdentifier)); } if (original == null && childNodes.isEmpty()) { return null; } return new CompositeNodeTOImpl(qname, null, childNodes); } finally { getDelegateReadLock().unlock(); } } } private class NormalizedDataModification extends AbstractDataModification<YangInstanceIdentifier, CompositeNode> { private final String CONFIGURATIONAL_DATA_STORE_MARKER = "configurational"; private final String OPERATIONAL_DATA_STORE_MARKER = "operational"; private final Object identifier; private TransactionStatus status; public NormalizedDataModification(final DataModification<YangInstanceIdentifier, CompositeNode> original) { super(getDelegate()); identifier = original; status = TransactionStatus.NEW; } /** * * Ensures all subpaths are removed - this currently does slow lookup in * all keys. * * @param entry */ public void deepRemoveOperationalData(final YangInstanceIdentifier entry) { Iterable<YangInstanceIdentifier> paths = getOperationalSubpaths(entry); removeOperationalData(entry); for (YangInstanceIdentifier potential : paths) { removeOperationalData(potential); } } public void deepRemoveConfigurationData(final YangInstanceIdentifier entry) { Iterable<YangInstanceIdentifier> paths = getConfigurationSubpaths(entry); removeConfigurationData(entry); for (YangInstanceIdentifier potential : paths) { removeConfigurationData(potential); } } public void putDeepConfigurationData(final YangInstanceIdentifier entryKey, final CompositeNode entryData) { this.putCompositeNodeData(entryKey, entryData, CONFIGURATIONAL_DATA_STORE_MARKER); } public void putDeepOperationalData(final YangInstanceIdentifier entryKey, final CompositeNode entryData) { this.putCompositeNodeData(entryKey, entryData, OPERATIONAL_DATA_STORE_MARKER); } @Override public Object getIdentifier() { return this.identifier; } @Override public TransactionStatus getStatus() { return status; } @Override public Future<RpcResult<TransactionStatus>> commit() { throw new UnsupportedOperationException("Commit should not be invoked on this"); } @Override protected CompositeNode mergeConfigurationData(final YangInstanceIdentifier path, final CompositeNode stored, final CompositeNode modified) { return mergeData(path, stored, modified, true); } @Override protected CompositeNode mergeOperationalData(final YangInstanceIdentifier path, final CompositeNode stored, final CompositeNode modified) { return mergeData(path, stored, modified, false); } private void putData(final YangInstanceIdentifier entryKey, final CompositeNode entryData, final String dataStoreIdentifier) { if (dataStoreIdentifier != null && entryKey != null && entryData != null) { switch (dataStoreIdentifier) { case (CONFIGURATIONAL_DATA_STORE_MARKER): this.putConfigurationData(entryKey, entryData); break; case (OPERATIONAL_DATA_STORE_MARKER): this.putOperationalData(entryKey, entryData); break; default: LOG.error(dataStoreIdentifier + " is NOT valid DataStore switch marker"); throw new RuntimeException(dataStoreIdentifier + " is NOT valid DataStore switch marker"); } } } private void putCompositeNodeData(final YangInstanceIdentifier entryKey, final CompositeNode entryData, final String dataStoreIdentifier) { this.putData(entryKey, entryData, dataStoreIdentifier); for (Node<?> child : entryData.getValue()) { YangInstanceIdentifier subEntryId = YangInstanceIdentifier.builder(entryKey) .node(child.getNodeType()).toInstance(); if (child instanceof CompositeNode) { DataSchemaNode subSchema = schemaNodeFor(subEntryId); CompositeNode compNode = (CompositeNode) child; YangInstanceIdentifier instanceId = null; if (subSchema instanceof ListSchemaNode) { ListSchemaNode listSubSchema = (ListSchemaNode) subSchema; Map<QName, Object> mapOfSubValues = this.getValuesFromListSchema(listSubSchema, (CompositeNode) child); if (mapOfSubValues != null) { instanceId = YangInstanceIdentifier.builder(entryKey) .nodeWithKey(listSubSchema.getQName(), mapOfSubValues).toInstance(); } } else if (subSchema instanceof ContainerSchemaNode) { ContainerSchemaNode containerSchema = (ContainerSchemaNode) subSchema; instanceId = YangInstanceIdentifier.builder(entryKey).node(subSchema.getQName()) .toInstance(); } if (instanceId != null) { this.putCompositeNodeData(instanceId, compNode, dataStoreIdentifier); } } } } private Map<QName, Object> getValuesFromListSchema(final ListSchemaNode listSchema, final CompositeNode entryData) { List<QName> keyDef = listSchema.getKeyDefinition(); if (keyDef != null && !keyDef.isEmpty()) { Map<QName, Object> map = new HashMap<QName, Object>(); for (QName key : keyDef) { List<Node<?>> data = entryData.get(key); if (data != null && !data.isEmpty()) { for (Node<?> nodeData : data) { if (nodeData instanceof SimpleNode<?>) { map.put(key, data.get(0).getValue()); } } } } return map; } return null; } } }