From: Tony Tkacik Date: Mon, 23 Feb 2015 11:17:50 +0000 (+0000) Subject: Merge "BUG-2635 Netconf monitoring for md-sal netconf northbound" X-Git-Tag: release/lithium~470^2~9 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=ecabb3cf9b22efd564ef71cb60918318af19d753;hp=7ab97f33c1d0c7da891337d8ec0b117555914115 Merge "BUG-2635 Netconf monitoring for md-sal netconf northbound" --- diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index 43ac4ae91c..cf4aa3140e 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -304,9 +304,10 @@ - com.jcabi - jcabi-maven-slf4j - 0.8 + org.apache.maven + maven-core + 3.1.1 + provided diff --git a/opendaylight/config/yang-jmx-generator-plugin/pom.xml b/opendaylight/config/yang-jmx-generator-plugin/pom.xml index 6c8a591bb8..ae190848c9 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/pom.xml +++ b/opendaylight/config/yang-jmx-generator-plugin/pom.xml @@ -21,11 +21,6 @@ guava - - com.jcabi - jcabi-maven-slf4j - - commons-io commons-io @@ -36,17 +31,6 @@ commons-lang3 - - org.codehaus.gmaven.runtime - gmaven-runtime-2.0 - - - org.sonatype.gossip - gossip - - - - org.opendaylight.controller yang-jmx-generator @@ -66,6 +50,10 @@ org.opendaylight.yangtools yang-maven-plugin-spi + + org.apache.maven + maven-core + org.slf4j diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java index 9ad8d2826f..1f1776f0a5 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java +++ b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java @@ -23,7 +23,6 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.FileUtils; -import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import org.opendaylight.controller.config.spi.ModuleFactory; import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; @@ -35,42 +34,54 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang2sources.spi.CodeGenerator; +import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator; +import org.opendaylight.yangtools.yang2sources.spi.MavenProjectAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.slf4j.impl.StaticLoggerBinder; /** * This class interfaces with yang-maven-plugin. Gets parsed yang modules in * {@link SchemaContext}, and parameters form the plugin configuration, and * writes service interfaces and/or modules. */ -public class JMXGenerator implements CodeGenerator { +public class JMXGenerator implements BasicCodeGenerator, MavenProjectAware { + private static final class NamespaceMapping { + private final String namespace, packageName; + public NamespaceMapping(final String namespace, final String packagename) { + this.namespace = namespace; + this.packageName = packagename; + } + } + + @VisibleForTesting static final String NAMESPACE_TO_PACKAGE_DIVIDER = "=="; + @VisibleForTesting static final String NAMESPACE_TO_PACKAGE_PREFIX = "namespaceToPackage"; + @VisibleForTesting static final String MODULE_FACTORY_FILE_BOOLEAN = "moduleFactoryFile"; + private static final Logger LOG = LoggerFactory.getLogger(JMXGenerator.class); + private static final Pattern NAMESPACE_MAPPING_PATTERN = Pattern.compile("(.+)" + NAMESPACE_TO_PACKAGE_DIVIDER + "(.+)"); + private PackageTranslator packageTranslator; private final CodeWriter codeWriter; - private static final Logger LOG = LoggerFactory - .getLogger(JMXGenerator.class); private Map namespaceToPackageMapping; private File resourceBaseDir; private File projectBaseDir; private boolean generateModuleFactoryFile = true; public JMXGenerator() { - this.codeWriter = new CodeWriter(); + this(new CodeWriter()); } - public JMXGenerator(CodeWriter codeWriter) { + public JMXGenerator(final CodeWriter codeWriter) { this.codeWriter = codeWriter; } @Override - public Collection generateSources(SchemaContext context, - File outputBaseDir, Set yangModulesInCurrentMavenModule) { + public Collection generateSources(final SchemaContext context, + final File outputBaseDir, final Set yangModulesInCurrentMavenModule) { Preconditions.checkArgument(context != null, "Null context received"); Preconditions.checkArgument(outputBaseDir != null, @@ -173,7 +184,8 @@ public class JMXGenerator implements CodeGenerator { return generatedFiles.getFiles(); } - static File concatFolders(File projectBaseDir, String... folderNames) { + @VisibleForTesting + static File concatFolders(final File projectBaseDir, final String... folderNames) { StringBuilder b = new StringBuilder(); for (String folder : folderNames) { b.append(folder); @@ -183,18 +195,14 @@ public class JMXGenerator implements CodeGenerator { } @Override - public void setAdditionalConfig(Map additionalCfg) { - if (LOG != null) { - LOG.debug(getClass().getCanonicalName(), - ": Additional configuration received: ", - additionalCfg.toString()); - } + public void setAdditionalConfig(final Map additionalCfg) { + LOG.debug("{}: Additional configuration received: {}", getClass().getCanonicalName(), additionalCfg); this.namespaceToPackageMapping = extractNamespaceMapping(additionalCfg); this.generateModuleFactoryFile = extractModuleFactoryBoolean(additionalCfg); } private boolean extractModuleFactoryBoolean( - Map additionalCfg) { + final Map additionalCfg) { String bool = additionalCfg.get(MODULE_FACTORY_FILE_BOOLEAN); if (bool == null) { return true; @@ -205,13 +213,8 @@ public class JMXGenerator implements CodeGenerator { return true; } - @Override - public void setLog(Log log) { - StaticLoggerBinder.getSingleton().setMavenLog(log); - } - private static Map extractNamespaceMapping( - Map additionalCfg) { + final Map additionalCfg) { Map namespaceToPackage = Maps.newHashMap(); for (String key : additionalCfg.keySet()) { if (key.startsWith(NAMESPACE_TO_PACKAGE_PREFIX)) { @@ -224,46 +227,30 @@ public class JMXGenerator implements CodeGenerator { return namespaceToPackage; } - static Pattern namespaceMappingPattern = Pattern.compile("(.+)" - + NAMESPACE_TO_PACKAGE_DIVIDER + "(.+)"); - - private static NamespaceMapping extractNamespaceMapping(String mapping) { - Matcher matcher = namespaceMappingPattern.matcher(mapping); - Preconditions - .checkArgument(matcher.matches(), String.format("Namespace to package mapping:%s is in invalid " + - "format, requested format is: %s", mapping, namespaceMappingPattern)); + private static NamespaceMapping extractNamespaceMapping(final String mapping) { + Matcher matcher = NAMESPACE_MAPPING_PATTERN.matcher(mapping); + Preconditions.checkArgument(matcher.matches(), + "Namespace to package mapping:%s is in invalid format, requested format is: %s", + mapping, NAMESPACE_MAPPING_PATTERN); return new NamespaceMapping(matcher.group(1), matcher.group(2)); } - private static class NamespaceMapping { - public NamespaceMapping(String namespace, String packagename) { - this.namespace = namespace; - this.packageName = packagename; - } - - private final String namespace, packageName; - } - @Override - public void setResourceBaseDir(File resourceDir) { + public void setResourceBaseDir(final File resourceDir) { this.resourceBaseDir = resourceDir; } @Override - public void setMavenProject(MavenProject project) { + public void setMavenProject(final MavenProject project) { this.projectBaseDir = project.getBasedir(); - - if (LOG != null) { - LOG.debug(getClass().getCanonicalName(), " project base dir: ", - projectBaseDir); - } + LOG.debug("{}: project base dir: {}", getClass().getCanonicalName(), projectBaseDir); } @VisibleForTesting static class GeneratedFilesTracker { private final Set files = Sets.newHashSet(); - void addFile(File file) { + void addFile(final File file) { if (files.contains(file)) { List undeletedFiles = Lists.newArrayList(); for (File presentFile : files) { @@ -283,7 +270,7 @@ public class JMXGenerator implements CodeGenerator { files.add(file); } - void addFile(Collection files) { + void addFile(final Collection files) { for (File file : files) { addFile(file); } diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java index 19e875f9b1..00454d8acf 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java +++ b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java @@ -48,9 +48,9 @@ import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Meth import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition; import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField; import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper; -import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil; import org.opendaylight.yangtools.sal.binding.model.api.ParameterizedType; import org.opendaylight.yangtools.sal.binding.model.api.Type; +import org.opendaylight.yangtools.yang.binding.BindingMapping; public class TemplateFactory { @@ -59,7 +59,7 @@ public class TemplateFactory { * bean as value that should be persisted from this instance. */ public static Map getTOAndMXInterfaceFtlFiles( - RuntimeBeanEntry entry) { + final RuntimeBeanEntry entry) { Map result = new HashMap<>(); { // create GeneralInterfaceFtlFile for runtime MXBean. Attributes will // be transformed to getter methods @@ -109,7 +109,7 @@ public class TemplateFactory { } // FIXME: put into Type.toString - static String serializeType(Type type, boolean addWildcards) { + static String serializeType(final Type type, final boolean addWildcards) { if (type instanceof ParameterizedType){ ParameterizedType parameterizedType = (ParameterizedType) type; StringBuilder sb = new StringBuilder(); @@ -131,11 +131,11 @@ public class TemplateFactory { } } - static String serializeType(Type type) { + static String serializeType(final Type type) { return serializeType(type, false); } - private static String getReturnType(AttributeIfc attributeIfc) { + private static String getReturnType(final AttributeIfc attributeIfc) { String returnType; if (attributeIfc instanceof TypedAttribute) { Type type = ((TypedAttribute) attributeIfc).getType(); @@ -151,7 +151,7 @@ public class TemplateFactory { } public static GeneralInterfaceTemplate serviceInterfaceFromSie( - ServiceInterfaceEntry sie) { + final ServiceInterfaceEntry sie) { List extendedInterfaces = Lists .newArrayList(AbstractServiceInterface.class.getCanonicalName()); @@ -177,7 +177,7 @@ public class TemplateFactory { } public static AbstractFactoryTemplate abstractFactoryTemplateFromMbe( - ModuleMXBeanEntry mbe) { + final ModuleMXBeanEntry mbe) { AbstractFactoryAttributesProcessor attrProcessor = new AbstractFactoryAttributesProcessor(); attrProcessor.processAttributes(mbe.getAttributes(), mbe.getPackageName()); @@ -191,7 +191,7 @@ public class TemplateFactory { } public static AbstractModuleTemplate abstractModuleTemplateFromMbe( - ModuleMXBeanEntry mbe) { + final ModuleMXBeanEntry mbe) { AbstractModuleAttributesProcessor attrProcessor = new AbstractModuleAttributesProcessor(mbe.getAttributes()); List moduleFields = attrProcessor.getModuleFields(); @@ -234,7 +234,7 @@ public class TemplateFactory { } public static StubFactoryTemplate stubFactoryTemplateFromMbe( - ModuleMXBeanEntry mbe) { + final ModuleMXBeanEntry mbe) { return new StubFactoryTemplate(getHeaderFromEntry(mbe), mbe.getPackageName(), mbe.getStubFactoryName(), mbe.getFullyQualifiedName(mbe.getAbstractFactoryName()) @@ -242,7 +242,7 @@ public class TemplateFactory { } public static GeneralInterfaceTemplate mXBeanInterfaceTemplateFromMbe( - ModuleMXBeanEntry mbe) { + final ModuleMXBeanEntry mbe) { MXBeanInterfaceAttributesProcessor attrProcessor = new MXBeanInterfaceAttributesProcessor(); attrProcessor.processAttributes(mbe.getAttributes()); GeneralInterfaceTemplate ifcTemplate = new GeneralInterfaceTemplate( @@ -254,7 +254,7 @@ public class TemplateFactory { } public static Map tOsFromMbe( - ModuleMXBeanEntry mbe) { + final ModuleMXBeanEntry mbe) { Map retVal = Maps.newHashMap(); TOAttributesProcessor processor = new TOAttributesProcessor(); processor.processAttributes(mbe.getAttributes()); @@ -275,7 +275,7 @@ public class TemplateFactory { } public static Map tOsFromRbe( - RuntimeBeanEntry rbe) { + final RuntimeBeanEntry rbe) { Map retVal = Maps.newHashMap(); TOAttributesProcessor processor = new TOAttributesProcessor(); Map yangPropertiesToTypesMap = Maps.newHashMap(rbe.getYangPropertiesToTypesMap()); @@ -316,7 +316,7 @@ public class TemplateFactory { return retVal; } - private static Header getHeaderFromEntry(AbstractEntry mbe) { + private static Header getHeaderFromEntry(final AbstractEntry mbe) { return new Header(mbe.getYangModuleName(), mbe.getYangModuleLocalname()); } @@ -326,7 +326,7 @@ public class TemplateFactory { private final List tos = Lists.newArrayList(); - void processAttributes(Map attributes) { + void processAttributes(final Map attributes) { for (Entry attrEntry : attributes.entrySet()) { AttributeIfc attributeIfc = attrEntry.getValue(); if (attributeIfc instanceof TOAttribute) { @@ -342,7 +342,7 @@ public class TemplateFactory { } } - private void createTOInternal(TOAttribute toAttribute) { + private void createTOInternal(final TOAttribute toAttribute) { Map attrs = toAttribute.getCapitalizedPropertiesToTypesMap(); // recursive processing of TO's attributes @@ -360,12 +360,12 @@ public class TemplateFactory { private List fields; private List methods; - public TOInternal(Type type, Map attrs) { + public TOInternal(final Type type, final Map attrs) { this(type.getFullyQualifiedName(), type.getName(), attrs, type.getPackageName()); } - public TOInternal(String fullyQualifiedName, String name, - Map attrs, String packageName) { + public TOInternal(final String fullyQualifiedName, final String name, + final Map attrs, final String packageName) { this.fullyQualifiedName = fullyQualifiedName; this.name = name; processAttrs(attrs, packageName); @@ -374,7 +374,7 @@ public class TemplateFactory { private final static String dependencyResolverVarName = "dependencyResolver"; private final static String dependencyResolverInjectMethodName = "injectDependencyResolver"; - private void processAttrs(Map attrs, String packageName) { + private void processAttrs(final Map attrs, final String packageName) { fields = Lists.newArrayList(); methods = Lists.newArrayList(); @@ -386,8 +386,7 @@ public class TemplateFactory { for (Entry attrEntry : attrs.entrySet()) { String innerName = attrEntry.getKey(); - String varName = BindingGeneratorUtil - .parseToValidParamName(attrEntry.getKey()); + String varName = BindingMapping.getPropertyName(attrEntry.getKey()); String fullyQualifiedName, nullableDefault = null; if (attrEntry.getValue() instanceof TypedAttribute) { @@ -449,7 +448,7 @@ public class TemplateFactory { private static class MXBeanInterfaceAttributesProcessor { private final List methods = Lists.newArrayList(); - void processAttributes(Map attributes) { + void processAttributes(final Map attributes) { for (Entry attrEntry : attributes.entrySet()) { String returnType; AttributeIfc attributeIfc = attrEntry.getValue(); @@ -473,8 +472,7 @@ public class TemplateFactory { MethodDeclaration getter = new MethodDeclaration(returnType, getterName, Collections. emptyList()); - String varName = BindingGeneratorUtil - .parseToValidParamName(attrEntry.getKey()); + String varName = BindingMapping.getPropertyName(attrEntry.getKey()); String setterName = "set" + attributeIfc.getUpperCaseCammelCase(); MethodDeclaration setter = new MethodDeclaration("void", @@ -519,8 +517,8 @@ public class TemplateFactory { private final List fields = Lists.newArrayList(); - void processAttributes(Map attributes, - String packageName) { + void processAttributes(final Map attributes, + final String packageName) { for (Entry attrEntry : attributes.entrySet()) { String type; String nullableDefaultWrapped = null; @@ -550,7 +548,7 @@ public class TemplateFactory { private final List moduleFields; private final List methods; - private Holder(List moduleFields, List methods) { + private Holder(final List moduleFields, final List methods) { this.moduleFields = Collections.unmodifiableList(moduleFields); this.methods = Collections.unmodifiableList(methods); } @@ -559,11 +557,11 @@ public class TemplateFactory { private final Holder holder; - private AbstractModuleAttributesProcessor(Map attributes) { + private AbstractModuleAttributesProcessor(final Map attributes) { this.holder = processAttributes(attributes); } - private static Holder processAttributes(Map attributes) { + private static Holder processAttributes(final Map attributes) { List moduleFields = new ArrayList<>(); List methods = new ArrayList<>(); for (Entry attrEntry : attributes.entrySet()) { @@ -607,8 +605,7 @@ public class TemplateFactory { } } - String varName = BindingGeneratorUtil - .parseToValidParamName(attrEntry.getKey()); + String varName = BindingMapping.getPropertyName(attrEntry.getKey()); ModuleField field; if (isIdentity) { @@ -689,7 +686,7 @@ public class TemplateFactory { } - private static boolean needsDepResolver(AttributeIfc value) { + private static boolean needsDepResolver(final AttributeIfc value) { if(value instanceof TOAttribute) { return true; } @@ -701,7 +698,7 @@ public class TemplateFactory { return false; } - private static String getInnerTypeFromIdentity(Type type) { + private static String getInnerTypeFromIdentity(final Type type) { Preconditions.checkArgument(type instanceof ParameterizedType); Type[] args = ((ParameterizedType) type).getActualTypeArguments(); Preconditions.checkArgument(args.length ==1); diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java index a6cfc58c34..3dae004161 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java +++ b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java @@ -16,11 +16,8 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; - import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; @@ -42,7 +39,6 @@ import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.commons.io.FileUtils; -import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.compiler.IProblem; @@ -124,13 +120,6 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { File targetDir = new File(generatorOutputPath, "target"); generatedResourcesDir = new File(targetDir, "generated-resources"); jmxGenerator.setResourceBaseDir(generatedResourcesDir); - Log mockedLog = mock(Log.class); - doReturn(false).when(mockedLog).isDebugEnabled(); - doNothing().when(mockedLog).debug(any(CharSequence.class)); - doNothing().when(mockedLog).info(any(CharSequence.class)); - doNothing().when(mockedLog).error(any(CharSequence.class), - any(Throwable.class)); - jmxGenerator.setLog(mockedLog); MavenProject project = mock(MavenProject.class); doReturn(generatorOutputPath).when(project).getBasedir(); jmxGenerator.setMavenProject(project); @@ -158,18 +147,19 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { verifyModuleFactoryFile(false); } - private void verifyModuleFactoryFile(boolean shouldBePresent) { + private void verifyModuleFactoryFile(final boolean shouldBePresent) { File factoryFile = new File(generatedResourcesDir, "META-INF" + File.separator + "services" + File.separator + ModuleFactory.class.getName()); - if (!shouldBePresent) + if (!shouldBePresent) { assertFalse("Factory file should not be generated", factoryFile.exists()); - else + } else { assertTrue("Factory file should be generated", factoryFile.exists()); + } } - public static List toFileNames(Collection files) { + public static List toFileNames(final Collection files) { List result = new ArrayList<>(); for (File f : files) { result.add(f.getName()); @@ -279,7 +269,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { new Predicate() { @Override - public boolean apply(File input) { + public boolean apply(final File input) { return input.getName().endsWith("xml"); } }); @@ -288,7 +278,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { new Predicate() { @Override - public boolean apply(File input) { + public boolean apply(final File input) { return input.getName().endsWith("java"); } }); @@ -303,16 +293,21 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { String name = file.getName(); MbeASTVisitor visitor = new MbeASTVisitor(); verifiers.put(name, visitor); - if (name.equals("AbstractDynamicThreadPoolModule.java")) + if (name.equals("AbstractDynamicThreadPoolModule.java")) { abstractDynamicThreadPoolModuleVisitor = visitor; - if (name.equals("AsyncEventBusModuleMXBean.java")) + } + if (name.equals("AsyncEventBusModuleMXBean.java")) { asyncEventBusModuleMXBeanVisitor = visitor; - if (name.equals("AbstractNamingThreadFactoryModuleFactory.java")) + } + if (name.equals("AbstractNamingThreadFactoryModuleFactory.java")) { abstractNamingThreadFactoryModuleFactoryVisitor = visitor; - if (name.equals("AsyncEventBusModule.java")) + } + if (name.equals("AsyncEventBusModule.java")) { asyncEventBusModuleVisitor = visitor; - if (name.equals("EventBusModuleFactory.java")) + } + if (name.equals("EventBusModuleFactory.java")) { eventBusModuleFactoryVisitor = visitor; + } } processGeneratedCode(javaFiles, verifiers); @@ -348,25 +343,25 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } - private void verifyXmlFiles(Collection xmlFiles) throws Exception { + private void verifyXmlFiles(final Collection xmlFiles) throws Exception { ErrorHandler errorHandler = new ErrorHandler() { @Override - public void warning(SAXParseException exception) + public void warning(final SAXParseException exception) throws SAXException { fail("Generated blueprint xml is not well formed " + exception.getMessage()); } @Override - public void fatalError(SAXParseException exception) + public void fatalError(final SAXParseException exception) throws SAXException { fail("Generated blueprint xml is not well formed " + exception.getMessage()); } @Override - public void error(SAXParseException exception) throws SAXException { + public void error(final SAXParseException exception) throws SAXException { fail("Generated blueprint xml is not well formed " + exception.getMessage()); } @@ -386,7 +381,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } - private void assertEventBusModuleFactory(MbeASTVisitor visitor) { + private void assertEventBusModuleFactory(final MbeASTVisitor visitor) { assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX + ".threads.java", visitor.packageName); assertEquals("EventBusModuleFactory", visitor.type); @@ -406,7 +401,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { visitor.methodJavadoc.size()); } - private void assertAsyncEventBusModule(MbeASTVisitor visitor) { + private void assertAsyncEventBusModule(final MbeASTVisitor visitor) { assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX + ".threads.java", visitor.packageName); assertEquals("AsyncEventBusModule", visitor.type); @@ -427,7 +422,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } private void assertAbstractNamingThreadFactoryModuleFactory( - MbeASTVisitor visitor) { + final MbeASTVisitor visitor) { assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX + ".threads.java", visitor.packageName); assertEquals("AbstractNamingThreadFactoryModuleFactory", visitor.type); @@ -450,7 +445,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } - private void assertFactoryMethods(Set methods, int expectedSize) { + private void assertFactoryMethods(final Set methods, final int expectedSize) { List args = Lists.newArrayList(); ArgumentAssertion oldInstanceArg = new ArgumentAssertion(DynamicMBeanWithInstance.class.getCanonicalName(), "old"); @@ -496,12 +491,12 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } - private void assertMethodPresent(Set methods, MethodAssertion methodAssertion) { + private void assertMethodPresent(final Set methods, final MethodAssertion methodAssertion) { assertTrue(String.format("Generated methods did not contain %s, generated methods: %s", methodAssertion.toString(), methods), methods.contains(methodAssertion.toString())); } - private void assertAsyncEventBusModuleMXBean(MbeASTVisitor visitor) { + private void assertAsyncEventBusModuleMXBean(final MbeASTVisitor visitor) { assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX + ".threads.java", visitor.packageName); assertEquals("AsyncEventBusModuleMXBean", visitor.type); @@ -511,7 +506,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } - private void assertAbstractDynamicThreadPoolModule(MbeASTVisitor visitor) { + private void assertAbstractDynamicThreadPoolModule(final MbeASTVisitor visitor) { assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX + ".threads.java", visitor.packageName); assertNotNull(visitor.javadoc); @@ -557,8 +552,8 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { visitor.methodJavadoc.get("void setMaximumSize(java.lang.Long maximumSize)")); } - private void assertDeclaredField(Set fieldDeclarations, - String declaration) { + private void assertDeclaredField(final Set fieldDeclarations, + final String declaration) { assertTrue("Missing field " + declaration + ", got: " + fieldDeclarations, fieldDeclarations.contains(declaration + ";\n")); @@ -570,13 +565,13 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { protected Map methodDescriptions = Maps.newHashMap(); @Override - public boolean visit(PackageDeclaration node) { + public boolean visit(final PackageDeclaration node) { packageName = node.getName().toString(); return super.visit(node); } @Override - public boolean visit(NormalAnnotation node) { + public boolean visit(final NormalAnnotation node) { if (node.getTypeName().toString() .equals(Description.class.getCanonicalName())) { if (node.getParent() instanceof TypeDeclaration) { @@ -604,7 +599,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } @Override - public boolean visit(TypeDeclaration node) { + public boolean visit(final TypeDeclaration node) { javadoc = node.getJavadoc() == null ? null : node.getJavadoc() .toString(); type = node.getName().toString(); @@ -624,7 +619,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { private final Map methodJavadoc = Maps.newHashMap(); @Override - public boolean visit(NormalAnnotation node) { + public boolean visit(final NormalAnnotation node) { boolean result = super.visit(node); if (node.getTypeName().toString() .equals(RequireInterface.class.getCanonicalName()) @@ -638,16 +633,16 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } @Override - public boolean visit(FieldDeclaration node) { + public boolean visit(final FieldDeclaration node) { fieldDeclarations.add(node.toString()); return super.visit(node); } @Override - public boolean visit(MethodDeclaration node) { - if (node.isConstructor()) + public boolean visit(final MethodDeclaration node) { + if (node.isConstructor()) { constructors.add(node.toString()); - else { + } else { String methodSignature = node.getReturnType2() + " " + node.getName() + "("; boolean first = true; for (Object o : node.parameters()) { @@ -668,7 +663,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } @Override - public boolean visit(TypeDeclaration node) { + public boolean visit(final TypeDeclaration node) { boolean visit = super.visit(node); List superIfcs = node.superInterfaceTypes(); implmts = superIfcs != null && !superIfcs.isEmpty() ? superIfcs @@ -680,14 +675,14 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } - private void assertContains(String source, String... contained) { + private void assertContains(final String source, final String... contained) { for (String string : contained) { assertThat(source, containsString(string)); } } - private void processGeneratedCode(Collection files, - Map verifiers) throws IOException { + private void processGeneratedCode(final Collection files, + final Map verifiers) throws IOException { ASTParser parser = ASTParser.newParser(AST.JLS3); Map options = JavaCore.getOptions(); JavaCore.setComplianceOptions(JavaCore.VERSION_1_7, options); @@ -705,27 +700,31 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { for (IProblem c : cu.getProblems()) { // 1610613332 = Syntax error, annotations are only available if // source level is 5.0 - if (c.getID() == 1610613332) + if (c.getID() == 1610613332) { continue; + } // 1610613332 = Syntax error, parameterized types are only // available if source level is 5.0 - if (c.getID() == 1610613329) + if (c.getID() == 1610613329) { continue; - if (c.getID() == 1610613328) // 'for each' statements are only available if source level is 5.0 + } + if (c.getID() == 1610613328) { continue; + } fail("Error in generated source code " + file + ":" + c.getSourceLineNumber() + " id: " + c.getID() + " message:" + c.toString()); } ASTVisitor visitor = verifiers.get(file.getName()); - if (visitor == null) + if (visitor == null) { fail("Unknown generated file " + file.getName()); + } cu.accept(visitor); } } - public static char[] readFileAsChars(File file) throws IOException { + public static char[] readFileAsChars(final File file) throws IOException { List readLines = Files .readLines(file, Charset.forName("utf-8")); char[] retVal = new char[0]; @@ -741,15 +740,15 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { private static class MethodAssertion extends ArgumentAssertion{ - private List arguments; + private final List arguments; - MethodAssertion(String type, String name, List arguments) { + MethodAssertion(final String type, final String name, final List arguments) { super(type, name); this.arguments = arguments; } - MethodAssertion(String type, String name) { + MethodAssertion(final String type, final String name) { this(type, name, Collections.emptyList()); } @@ -763,8 +762,9 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { for (ArgumentAssertion argument : arguments) { sb.append(argument.type).append(' '); sb.append(argument.name); - if(++i != arguments.size()) + if(++i != arguments.size()) { sb.append(','); + } } sb.append(')'); return sb.toString(); @@ -775,7 +775,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { protected final String type, name; - private ArgumentAssertion(String type, String name) { + private ArgumentAssertion(final String type, final String name) { this.type = type; this.name = name; } diff --git a/opendaylight/md-sal/sal-dom-api/pom.xml b/opendaylight/md-sal/sal-dom-api/pom.xml index 0302a7d920..89fca50354 100644 --- a/opendaylight/md-sal/sal-dom-api/pom.xml +++ b/opendaylight/md-sal/sal-dom-api/pom.xml @@ -33,6 +33,20 @@ org.osgi org.osgi.core + + + junit + junit + + + org.opendaylight.controller + sal-test-model + + + org.opendaylight.yangtools + yang-data-impl + test + diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeInaccessibleException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeInaccessibleException.java new file mode 100644 index 0000000000..eea1be51bb --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeInaccessibleException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015 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.md.sal.dom.api; + +import com.google.common.base.Preconditions; + + +/** + * Failure reported when a data tree is no longer accessible. + */ +public class DOMDataTreeInaccessibleException extends DOMDataTreeListeningException { + private static final long serialVersionUID = 1L; + private final DOMDataTreeIdentifier treeIdentifier; + + public DOMDataTreeInaccessibleException(final DOMDataTreeIdentifier treeIdentifier, final String message) { + super(message); + this.treeIdentifier = Preconditions.checkNotNull(treeIdentifier); + } + + public DOMDataTreeInaccessibleException(final DOMDataTreeIdentifier treeIdentifier, final String message, final Throwable cause) { + super(message); + this.treeIdentifier = Preconditions.checkNotNull(treeIdentifier); + } + + public final DOMDataTreeIdentifier getTreeIdentifier() { + return treeIdentifier; + } +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListener.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListener.java new file mode 100644 index 0000000000..083cd10ef3 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListener.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015 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.md.sal.dom.api; + +import java.util.Collection; +import java.util.EventListener; +import java.util.Map; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; + +/** + * Interface implemented by data consumers, e.g. processes wanting to act on data + * after it has been introduced to the conceptual data tree. + */ +public interface DOMDataTreeListener extends EventListener { + /** + * Invoked whenever one or more registered subtrees change. The logical changes are reported, + * as well as the roll up of new state for all subscribed subtrees. + * + * @param changes The set of changes being reported. Each subscribed subtree may be present + * at most once. + * @param subtrees Per-subtree state as visible after the reported changes have been applied. + * This includes all the subtrees this listener is subscribed to, even those + * which have not changed. + */ + void onDataTreeChanged(@Nonnull Collection changes, @Nonnull Map> subtrees); + + /** + * Invoked when a subtree listening failure occurs. This can be triggered, for example, when + * a connection to external subtree source is broken. The listener will not receive any other + * callbacks, but its registration still needs to be closed to prevent resource leak. + * + * @param cause Collection of failure causes, may not be null or empty. + */ + void onDataTreeFailed(@Nonnull Collection causes); +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListeningException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListeningException.java new file mode 100644 index 0000000000..e0d9b427d9 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListeningException.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2015 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.md.sal.dom.api; + +/** + * Base exception for various causes why and {@link DOMDataTreeListener} + * may be terminated by the {@link DOMDataTreeService} implementation. + */ +public class DOMDataTreeListeningException extends Exception { + private static final long serialVersionUID = 1L; + + public DOMDataTreeListeningException(final String message) { + super(message); + } + + public DOMDataTreeListeningException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeLoopException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeLoopException.java new file mode 100644 index 0000000000..8f498a887d --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeLoopException.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015 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.md.sal.dom.api; + +import javax.annotation.Nonnull; + +/** + * Exception thrown when a loop is detected in the way {@link DOMDataTreeListener} + * and {@link DOMDataTreeProducer} instances would be connected. + */ +public class DOMDataTreeLoopException extends Exception { + private static final long serialVersionUID = 1L; + + public DOMDataTreeLoopException(final @Nonnull String message) { + super(message); + } + + public DOMDataTreeLoopException(final @Nonnull String message, final @Nonnull Throwable cause) { + super(message, cause); + } +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducer.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducer.java new file mode 100644 index 0000000000..cbfa0122f2 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducer.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015 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.md.sal.dom.api; + +import java.util.Collection; +import javax.annotation.Nonnull; + +/** + * A data producer context. It allows transactions to be submitted to the subtrees + * specified at instantiation time. At any given time there may be a single transaction + * open. It needs to be either submitted or cancelled before another one can be open. + * Once a transaction is submitted, it will proceed to be committed asynchronously. + * + * Each instance has an upper bound on the number of transactions which can be in-flight, + * once that capacity is exceeded, an attempt to create a new transaction will block + * until some transactions complete. + * + * Each {@link DOMDataTreeProducer} can be in two logical states, bound and unbound, + * which define the lifecycle rules for when is it legal to create and submit transactions + * in relationship with {@link DOMDataTreeListener} callbacks. + * + * When a producer is first created, it is unbound. In this state the producer can be + * accessed by any application thread to allocate or submit transactions, as long as + * the 'single open transaction' rule is maintained. The producer and any transaction + * object MUST NOT be accessed, directly or indirectly, from a {@link DOMDataTreeListener} + * callback. + * + * When a producer is referenced in a call to {@link DOMDataTreeService#registerListener(DOMDataTreeListener, java.util.Collection, boolean, java.util.Collection)}, + * an attempt will be made to bind the producer to the specified {@link DOMDataTreeListener}. + * Such an attempt will fail the producer is already bound, or it has an open transaction. + * Once bound, the producer can only be accessed from within the {@link DOMDataTreeListener} + * callback on that particular instance. Any transaction which is not submitted by the + * time the callback returns will be implicitly cancelled. A producer becomes unbound + * when the listener it is bound to becomes unregistered. + */ +public interface DOMDataTreeProducer extends DOMDataTreeProducerFactory, AutoCloseable { + /** + * Allocate a new open transaction on this producer. Any and all transactions + * previously allocated must have been either submitted or cancelled by the + * time this method is invoked. + * + * @param barrier Indicates whether this transaction should be a barrier. A barrier + * transaction is processed separately from any preceding transactions. + * Non-barrier transactions may be merged and processed in a batch, + * such that any observers see the modifications contained in them as + * if the modifications were made in a single transaction. + * @return A new {@link DOMDataWriteTransaction} + * @throws {@link IllegalStateException} if a previous transaction was not closed. + * @throws {@link IllegalThreadStateException} if the calling thread context does not + * match the lifecycle rules enforced by the producer state (e.g. bound or unbound). + * This exception is thrown on a best effort basis and programs should not rely + * on it for correct operation. + */ + @Nonnull DOMDataWriteTransaction createTransaction(boolean isolated); + + /** + * {@inheritDoc} + * + * When invoked on a {@link DOMDataTreeProducer}, this method has additional restrictions. + * There may not be an open transaction from this producer. The method needs to be + * invoked in appropriate context, e.g. bound or unbound. + * + * Specified subtrees must be accessible by this producer. Accessible means they are a subset + * of the subtrees specified when the producer is instantiated. The set is further reduced as + * child producers are instantiated -- if you create a producer for /a and then a child for + * /a/b, /a/b is not accessible from the first producer. + * + * Once this method returns successfully, this (parent) producer loses the ability to + * access the specified paths until the resulting (child) producer is shut down. + * + * @throws {@link IllegalStateException} if there is an open transaction + * @throws {@link IllegalArgumentException} if subtrees contains a subtree which is not + * accessible by this producer + * @throws {@link IllegalThreadStateException} if the calling thread context does not + * match the lifecycle rules enforced by the producer state (e.g. bound or unbound). + * This exception is thrown on a best effort basis and programs should not rely + * on it for correct operation. + */ + @Override + @Nonnull DOMDataTreeProducer createProducer(@Nonnull Collection subtrees); + + /** + * {@inheritDoc} + * + * @throws DOMDataTreeProducerBusyException when there is an open transaction. + */ + @Override + void close() throws DOMDataTreeProducerException; +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerBusyException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerBusyException.java new file mode 100644 index 0000000000..a83a5ca3d2 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerBusyException.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2015 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.md.sal.dom.api; + +/** + * Exception indicating that the {@link DOMDataTreeProducer} has an open user + * transaction and cannot be closed. + */ +public class DOMDataTreeProducerBusyException extends DOMDataTreeProducerException { + private static final long serialVersionUID = 1L; + + public DOMDataTreeProducerBusyException(final String message) { + super(message); + } + + public DOMDataTreeProducerBusyException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerException.java new file mode 100644 index 0000000000..16c26760ff --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerException.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2015 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.md.sal.dom.api; + +/** + * Base exception for all exceptions related to {@link DOMDataTreeProducer}s. + */ +public class DOMDataTreeProducerException extends Exception { + private static final long serialVersionUID = 1L; + + public DOMDataTreeProducerException(final String message) { + super(message); + } + + public DOMDataTreeProducerException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerFactory.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerFactory.java new file mode 100644 index 0000000000..89ac8d1e6c --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerFactory.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015 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.md.sal.dom.api; + +import java.util.Collection; +import javax.annotation.Nonnull; + +/** + * Base source of {@link DOMDataTreeProducer}s. This interface is usually not used directly, + * but rather through one of its sub-interfaces. + */ +public interface DOMDataTreeProducerFactory { + /** + * Create a producer, which is able to access to a set of trees. + * + * @param subtrees The collection of subtrees the resulting producer should have access to. + * @return A {@link DOMDataTreeProducer} instance. + * @throws {@link IllegalArgumentException} if subtrees is empty. + */ + @Nonnull DOMDataTreeProducer createProducer(@Nonnull Collection subtrees); +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeService.java new file mode 100644 index 0000000000..21ff44c539 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeService.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015 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.md.sal.dom.api; + +import java.util.Collection; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.ListenerRegistration; + +/** + * A {@link DOMService} providing access to the conceptual data tree. Interactions + * with the data tree are split into data producers and consumers (listeners). Each + * of them operate on a set of subtrees, which need to be declared at instantiation time. + * + * Returned instances are not thread-safe and expected to be used by a single thread + * at a time. Furthermore, producers may not be accessed from consumer callbacks + * unless they were specified when the listener is registered. + * + * The service maintains a loop-free topology of producers and consumers. What this means + * is that a consumer is not allowed to access a producer, which affects any of the + * subtrees it is subscribed to. This restriction is in place to ensure the system does + * not go into a feedback loop, where it is impossible to block either a producer or + * a consumer without accumulating excess work in the backlog stemming from its previous + * activity. + */ +public interface DOMDataTreeService extends DOMDataTreeProducerFactory, DOMService { + /** + * Register a {@link DOMDataTreeListener} instance. Once registered, the listener + * will start receiving changes on the selected subtrees. If the listener cannot + * keep up with the rate of changes, and allowRxMerges is set to true, this service + * is free to merge the changes, so that a smaller number of them will be reported, + * possibly hiding some data transitions (like flaps). + * + * If the listener wants to write into any producer, that producer has to be mentioned + * in the call to this method. Those producers will be bound exclusively to the + * registration, so that accessing them outside of this listener's callback will trigger + * an error. Any producers mentioned must be idle, e.g. they may not have an open + * transaction at the time this method is invoked. + * + * Each listener instance can be registered at most once. Implementations of this + * interface have to guarantee that the listener's methods will not be invoked + * concurrently from multiple threads. + * + * @param listener {@link DOMDataTreeListener} that is being registered + * @param subtrees Conceptual subtree identifier of subtrees which should be monitored + * for changes. May not be null or empty. + * @param allowRxMerges True if the backend may perform ingress state compression. + * @param producers {@link DOMDataTreeProducer} instances to bind to the listener. + * @return A listener registration. Once closed, the listener will no longer be + * invoked and the producers will be unbound. + * @throws IllegalArgumentException if subtrees is empty or the listener is already bound + * @throws DOMDataTreeLoopException if the registration of the listener to the specified + * subtrees with specified producers would form a + * feedback loop + */ + @Nonnull ListenerRegistration registerListener(@Nonnull T listener, + @Nonnull Collection subtrees, boolean allowRxMerges, @Nonnull Collection producers); +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShard.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShard.java new file mode 100644 index 0000000000..e9ef0034ea --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShard.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015 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.md.sal.dom.api; + +import java.util.EventListener; +import javax.annotation.Nonnull; + +/** + * A single shard of the conceptual data tree. This interface defines the basic notifications + * a shard can receive. Each shard implementation is expected to also implement some of the + * datastore-level APIs. Which interfaces are required depends on the {@link DOMDataTreeShardingService} + * implementation. + */ +public interface DOMDataTreeShard extends EventListener { + /** + * Invoked whenever a child is getting attached as a more specific prefix under this shard. + * + * @param prefix Child's prefix + * @param child Child shard + */ + void onChildAttached(@Nonnull DOMDataTreeIdentifier prefix, @Nonnull DOMDataTreeShard child); + + /** + * Invoked whenever a child is getting detached as a more specific prefix under this shard. + * + * @param prefix Child's prefix + * @param child Child shard + */ + void onChildDetached(@Nonnull DOMDataTreeIdentifier prefix, @Nonnull DOMDataTreeShard child); +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingConflictException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingConflictException.java new file mode 100644 index 0000000000..d44316d39d --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingConflictException.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015 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.md.sal.dom.api; + +import javax.annotation.Nonnull; + +/** + * Exception thrown when an attempt to attach a conflicting shard to the global + * table. + */ +public class DOMDataTreeShardingConflictException extends Exception { + private static final long serialVersionUID = 1L; + + public DOMDataTreeShardingConflictException(final @Nonnull String message) { + super(message); + } + + public DOMDataTreeShardingConflictException(final @Nonnull String message, final @Nonnull Throwable cause) { + super(message, cause); + } +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingService.java new file mode 100644 index 0000000000..c087224090 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingService.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015 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.md.sal.dom.api; + +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.ListenerRegistration; + +/** + * A {@link DOMService} providing access to details on how the conceptual data tree + * is distributed among providers (also known as shards). Each shard is tied to a + * single {@link DOMDataTreeIdentifier}. Based on those data tree identifiers, the + * shards are organized in a tree, where there is a logical parent/child relationship. + * + * It is not allowed to attach two shards to the same data tree identifier, which means + * the mapping of each piece of information has an unambiguous home. When accessing + * the information, the shard with the longest matching data tree identifier is used, + * which is why this interface treats it is a prefix. + * + * Whenever a parent/child relationship is changed, the parent is notified, so it can + * understand that a logical child has been attached. + */ +public interface DOMDataTreeShardingService extends DOMService { + /** + * Register a shard as responsible for a particular subtree prefix. + * + * @param prefix Data tree identifier, may not be null. + * @param shard Responsible shard instance + * @return A registration. To remove the shard's binding, close the registration. + * @throws DOMDataTreeShardingConflictException if the prefix is already bound + */ + @Nonnull ListenerRegistration registerDataTreeShard(@Nonnull DOMDataTreeIdentifier prefix, @Nonnull T shard) throws DOMDataTreeShardingConflictException; +} diff --git a/opendaylight/md-sal/sal-dom-api/src/test/java/org/opendaylight/controller/md/sal/dom/api/AbstractDOMDataTreeServiceTestSuite.java b/opendaylight/md-sal/sal-dom-api/src/test/java/org/opendaylight/controller/md/sal/dom/api/AbstractDOMDataTreeServiceTestSuite.java new file mode 100644 index 0000000000..896c606eb9 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/test/java/org/opendaylight/controller/md/sal/dom/api/AbstractDOMDataTreeServiceTestSuite.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2015 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.md.sal.dom.api; + +import static org.junit.Assert.assertNotNull; +import com.google.common.util.concurrent.CheckedFuture; +import java.net.URI; +import java.util.Collections; +import javax.annotation.Nonnull; +import org.junit.Test; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; + +/** + * Abstract test suite demonstrating various access patterns on how a {@link DOMDataTreeService} + * can be used. + */ +public abstract class AbstractDOMDataTreeServiceTestSuite { + protected static final QNameModule TEST_MODULE = QNameModule.create(URI.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:store"), null); + + protected static final YangInstanceIdentifier UNORDERED_CONTAINER_IID = YangInstanceIdentifier.create( + new NodeIdentifier(QName.create(TEST_MODULE, "lists")), + new NodeIdentifier(QName.create(TEST_MODULE, "unordered-container"))); + protected static final DOMDataTreeIdentifier UNORDERED_CONTAINER_TREE = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, UNORDERED_CONTAINER_IID); + + /** + * Return a reference to the service used in this test. The instance + * needs to be reused within the same test and must be isolated between + * tests. + * + * @return {@link DOMDataTreeService} instance. + */ + protected abstract @Nonnull DOMDataTreeService service(); + + /** + * A simple unbound producer. It write some basic things into the data store based on the + * test model. + * @throws DOMDataTreeProducerException + * @throws TransactionCommitFailedException + */ + @Test + public final void testBasicProducer() throws DOMDataTreeProducerException, TransactionCommitFailedException { + // Create a producer. It is an AutoCloseable resource, hence the try-with pattern + try (final DOMDataTreeProducer prod = service().createProducer(Collections.singleton(UNORDERED_CONTAINER_TREE))) { + assertNotNull(prod); + + final DOMDataWriteTransaction tx = prod.createTransaction(true); + assertNotNull(tx); + + tx.put(LogicalDatastoreType.OPERATIONAL, UNORDERED_CONTAINER_IID, ImmutableContainerNodeBuilder.create().build()); + + final CheckedFuture f = tx.submit(); + assertNotNull(f); + + f.checkedGet(); + } + } + + // TODO: simple listener +} diff --git a/opendaylight/md-sal/sal-dom-xsql/pom.xml b/opendaylight/md-sal/sal-dom-xsql/pom.xml index c7fd20248d..f0e27cf2ac 100644 --- a/opendaylight/md-sal/sal-dom-xsql/pom.xml +++ b/opendaylight/md-sal/sal-dom-xsql/pom.xml @@ -48,6 +48,10 @@ org.osgi org.osgi.core + + junit + junit +