Merge "Updated creation of Binding Spec Context to provide additional information."
authorRobert Varga <rovarga@cisco.com>
Sun, 3 Aug 2014 10:36:24 +0000 (10:36 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Sun, 3 Aug 2014 10:36:24 +0000 (10:36 +0000)
95 files changed:
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/TransformerGenerator.xtend
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/util/ClassLoaderUtils.java [deleted file]
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/BaseTemplate.xtend
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/ClassTemplate.xtend
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/util/ClassLoaderUtils.java [deleted file]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/util/ListenerRegistry.java
common/features/pom.xml
common/parent/pom.xml
common/util/pom.xml
common/util/src/main/java/org/opendaylight/yangtools/util/ClassLoaderUtils.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/ExecutorServiceUtil.java
common/util/src/main/java/org/opendaylight/yangtools/util/Immutables.java [moved from common/concepts/src/main/java/org/opendaylight/yangtools/concepts/util/Immutables.java with 97% similarity]
common/util/src/main/java/org/opendaylight/yangtools/util/ListenerRegistry.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/PropertyUtils.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListenableFutureTask.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListeningExecutorService.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CachedThreadPoolExecutor.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorService.java
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/FastThreadPoolExecutor.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/NotificationManager.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManager.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SpecialExecutors.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListeningExecutorServiceTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/CommonTestUtils.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorServiceTest.java
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManagerTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ThreadPoolExecutorTest.java [new file with mode: 0644]
yang/pom.xml
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/ClassLoaderUtils.java
yang/yang-data-composite-node/pom.xml [moved from yang/yang-data-json/pom.xml with 76% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/AnyXmlNodeCnSnParser.java [new file with mode: 0644]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/AugmentationNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/AugmentationNodeCnSnParser.java with 88% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/ChoiceNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/ChoiceNodeCnSnParser.java with 87% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/CnSnToNormalizedNodeParserFactory.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/CnSnToNormalizedNodeParserFactory.java with 90% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/ContainerNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/ContainerNodeCnSnParser.java with 89% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/LeafNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/LeafNodeCnSnParser.java with 92% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/LeafSetEntryNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/LeafSetEntryNodeCnSnParser.java with 93% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/LeafSetNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/LeafSetNodeCnSnParser.java with 93% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/MapEntryNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/MapEntryNodeCnSnParser.java with 89% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/MapNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/MapNodeCnSnParser.java with 93% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/AnyXmlNodeCnSnSerializer.java [new file with mode: 0644]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/AugmentationNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/AugmentationNodeCnSnSerializer.java with 92% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/ChoiceNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/ChoiceNodeCnSnSerializer.java with 92% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/CnSnFromNormalizedNodeSerializerFactory.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/CnSnFromNormalizedNodeSerializerFactory.java with 89% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/ContainerNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/ContainerNodeCnSnSerializer.java with 62% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/LeafNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/LeafNodeCnSnSerializer.java with 91% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/LeafSetEntryNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/LeafSetEntryNodeCnSnSerializer.java with 92% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/LeafSetNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/LeafSetNodeCnSnSerializer.java with 93% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/MapEntryNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/MapEntryNodeCnSnSerializer.java with 60% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/MapNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/MapNodeCnSnSerializer.java with 93% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/json/CnSnToNormalizedNodesUtils.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/json/CnSnToNormalizedNodesUtils.java with 93% similarity]
yang/yang-data-composite-node/src/test/java/org/opendaylight/yangtools/yang/data/composite/node/schema/TestUtils.java [moved from yang/yang-data-json/src/test/java/org/opendaylight/yangtools/yang/data/json/schema/TestUtils.java with 69% similarity]
yang/yang-data-composite-node/src/test/java/org/opendaylight/yangtools/yang/data/composite/node/schema/parser/ParseCnSnStructToNormalizedStructTest.java [moved from yang/yang-data-json/src/test/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/ParseCnSnStructToNormalizedStructTest.java with 87% similarity]
yang/yang-data-composite-node/src/test/java/org/opendaylight/yangtools/yang/data/composite/node/schema/serializer/SerializeNormalizedStructToCnSnStructTest.java [moved from yang/yang-data-json/src/test/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/SerializeNormalizedStructToCnSnStructTest.java with 88% similarity]
yang/yang-data-composite-node/src/test/resources/cnsn-to-normalized-node/json/simple-container.json [moved from yang/yang-data-json/src/test/resources/cnsn-to-normalized-node/json/simple-container.json with 82% similarity]
yang/yang-data-composite-node/src/test/resources/cnsn-to-normalized-node/yang/augment-simple-container.yang [moved from yang/yang-data-json/src/test/resources/cnsn-to-normalized-node/yang/augment-simple-container.yang with 100% similarity]
yang/yang-data-composite-node/src/test/resources/cnsn-to-normalized-node/yang/simple-container.yang [moved from yang/yang-data-json/src/test/resources/cnsn-to-normalized-node/yang/simple-container.yang with 92% similarity]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/FromNormalizedNodeSerializerFactory.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/ToNormalizedNodeParserFactory.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/parser/AnyXmlNodeBaseParser.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/parser/NodeParserDispatcher.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/serializer/AnyXmlNodeBaseSerializer.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/serializer/NodeSerializerDispatcher.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/parser/DomToNormalizedNodeParserFactory.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/serializer/DomFromNormalizedNodeSerializerFactory.java
yang/yang-maven-plugin-it/src/test/resources/test-parent/AdditionalConfig/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/Correct/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest1/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest2/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/Generator/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/InvalidVersion/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/MissingYangInDep/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/NamingConflict/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/NoGenerators/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/NoOutputDir/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/NoYangFiles/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/UnknownGenerator/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/YangRootNotExist/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/pom.xml
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceRegistration.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaTransformerRegistration.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/antlr/YangLexer.g4
yang/yang-parser-impl/src/main/antlr/YangParser.g4
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/ParserListenerUtils.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangModelBasicValidationListener.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/util/YangModelDependencyInfo.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolver.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLSchemaContextResolver.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/impl/TypesResolutionTest.java
yang/yang-parser-impl/src/test/resources/types/union-with-ext/extdef.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/types/union-with-ext/unionbug.yang [new file with mode: 0644]

index ab6b734c0bb540fdefb7bd5a94b1fa98c0cee7b8..769a9e5bb77c054510fdca274afbeb3d44a91d12 100644 (file)
@@ -29,7 +29,6 @@ import javassist.CtMethod
 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil
 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
 import org.opendaylight.yangtools.binding.generator.util.Types
-import org.opendaylight.yangtools.sal.binding.generator.util.ClassLoaderUtils
 import org.opendaylight.yangtools.sal.binding.generator.util.CodeGenerationException
 import org.opendaylight.yangtools.sal.binding.generator.util.XtendHelper
 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
@@ -39,6 +38,7 @@ import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
 import org.opendaylight.yangtools.sal.binding.model.api.ParameterizedType
 import org.opendaylight.yangtools.sal.binding.model.api.Type
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder
+import org.opendaylight.yangtools.util.ClassLoaderUtils
 import org.opendaylight.yangtools.yang.binding.Augmentation
 import org.opendaylight.yangtools.yang.binding.BindingCodec
 import org.opendaylight.yangtools.yang.binding.BindingDeserializer
diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/util/ClassLoaderUtils.java b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/util/ClassLoaderUtils.java
deleted file mode 100644 (file)
index 01ed2f0..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.yangtools.sal.binding.generator.util;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.locks.Lock;
-
-import com.google.common.base.Joiner;
-import com.google.common.base.Optional;
-
-/**
- * @deprecated Use {@link org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils} instead.
- */
-@Deprecated
-public final class ClassLoaderUtils {
-
-    private ClassLoaderUtils() {
-        throw new UnsupportedOperationException("Utility class");
-    }
-
-    public static <V> V withClassLoader(final ClassLoader cls, final Callable<V> function) throws Exception {
-        checkNotNull(cls, "Classloader should not be null");
-        checkNotNull(function, "Function should not be null");
-
-        final ClassLoader oldCls = Thread.currentThread().getContextClassLoader();
-        try {
-            Thread.currentThread().setContextClassLoader(cls);
-            return function.call();
-        } finally {
-            Thread.currentThread().setContextClassLoader(oldCls);
-        }
-    }
-
-    public static <V> V withClassLoaderAndLock(final ClassLoader cls, final Lock lock, final Callable<V> function) throws Exception {
-        checkNotNull(lock, "Lock should not be null");
-
-        lock.lock();
-        try {
-            return withClassLoader(cls, function);
-        } finally {
-            lock.unlock();
-        }
-    }
-
-    /**
-     * @deprecated Use one of the other utility methods.
-     */
-    @Deprecated
-    public static <V> V withClassLoaderAndLock(final ClassLoader cls, final Optional<Lock> lock, final Callable<V> function) throws Exception {
-        if (lock.isPresent()) {
-            return withClassLoaderAndLock(cls, lock.get(), function);
-        } else {
-            return withClassLoader(cls, function);
-        }
-    }
-
-    public static Object construct(final Constructor<? extends Object> constructor, final List<Object> objects)
-            throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
-        Object[] initargs = objects.toArray(new Object[] {});
-        return constructor.newInstance(initargs);
-    }
-
-
-    public static Class<?> loadClass(final ClassLoader cls, final String name) throws ClassNotFoundException {
-        if ("byte[]".equals(name)) {
-            return byte[].class;
-        } else if("char[]".equals(name)) {
-            return char[].class;
-        }
-        try {
-            return cls.loadClass(name);
-        } catch (ClassNotFoundException e) {
-            String[] components = name.split("\\.");
-            String potentialOuter;
-            int length = components.length;
-            if (length > 2 && (potentialOuter = components[length - 2]) != null && Character.isUpperCase(potentialOuter.charAt(0))) {
-
-                    String outerName = Joiner.on(".").join(Arrays.asList(components).subList(0, length - 1));
-                    String innerName = outerName + "$" + components[length-1];
-                    return cls.loadClass(innerName);
-            } else {
-                throw e;
-            }
-        }
-    }
-
-    public static Class<?> loadClassWithTCCL(final String name) throws ClassNotFoundException {
-        return loadClass(Thread.currentThread().getContextClassLoader(), name);
-    }
-
-    public static Class<?> tryToLoadClassWithTCCL(final String fullyQualifiedName) {
-        try {
-            return loadClassWithTCCL(fullyQualifiedName);
-        } catch (ClassNotFoundException e) {
-            return null;
-        }
-    }
-}
index 24d0a7fd6031f30b2410033337f7f6bef89f59f4..eb7b948c545283c6e7e3d162a202574365eb0723 100644 (file)
@@ -498,15 +498,15 @@ abstract class BaseTemplate {
             Â«val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
             public static Â«List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> Â«methodName»() {
                 Â«IF numberClass.equals(typeof(BigDecimal))»
-                    Â«lengthMethodBody(restrictions, numberClass, className, varName)»
+                    Â«lengthBody(restrictions, numberClass, className, varName)»
                 Â«ELSE»
-                    Â«lengthMethodBody(restrictions, typeof(BigInteger), className, varName)»
+                    Â«lengthBody(restrictions, typeof(BigInteger), className, varName)»
                 Â«ENDIF»
             }
         Â«ENDIF»
     '''
 
-    def private lengthMethodBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
+    def private lengthBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
         if («varName» == null) {
             synchronized («className».class) {
                 if («varName» == null) {
@@ -526,9 +526,9 @@ abstract class BaseTemplate {
             Â«val number = returnType.importedNumber»
             public static Â«List.importedName»<«Range.importedName»<«number»>> Â«methodName»() {
                 Â«IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
-                    Â«rangeMethodBody(restrictions, BigDecimal, className, varName)»
+                    Â«rangeBody(restrictions, BigDecimal, className, varName)»
                 Â«ELSE»
-                    Â«rangeMethodBody(restrictions, BigInteger, className, varName)»
+                    Â«rangeBody(restrictions, BigInteger, className, varName)»
                 Â«ENDIF»
             }
         Â«ENDIF»
@@ -539,15 +539,15 @@ abstract class BaseTemplate {
             Â«val returnType = properties.iterator.next.returnType»
             public static Â«List.importedName»<«Range.importedName»<«returnType.importedNumber»>> Â«methodName»() {
                 Â«IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
-                    Â«rangeMethodBody(restrictions, BigDecimal, className, varName)»
+                    Â«rangeBody(restrictions, BigDecimal, className, varName)»
                 Â«ELSE»
-                    Â«rangeMethodBody(restrictions, BigInteger, className, varName)»
+                    Â«rangeBody(restrictions, BigInteger, className, varName)»
                 Â«ENDIF»
             }
         Â«ENDIF»
     '''
 
-    def private rangeMethodBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
+    def private rangeBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
         if («varName» == null) {
             synchronized («className».class) {
                 if («varName» == null) {
@@ -576,7 +576,7 @@ abstract class BaseTemplate {
         return BigInteger.importedName
     }
 
-    def private String numericValue(Class<? extends Number> clazz, Object numberValue) {
+    def protected String numericValue(Class<? extends Number> clazz, Object numberValue) {
         val number = clazz.importedName;
         val value = numberValue.toString
         if (clazz.equals(typeof(BigInteger)) || clazz.equals(typeof(BigDecimal))) {
index e92f84e489ae1e3db0bea4faf4569b3ae2e8f304..afebd5846b156465d9ec7de1049124190696a7a1 100644 (file)
@@ -7,24 +7,28 @@
  */
 package org.opendaylight.yangtools.sal.java.api.generator
 
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.Lists
+import com.google.common.collect.Range
+import com.google.common.io.BaseEncoding
+import java.beans.ConstructorProperties
+import java.math.BigDecimal
+import java.math.BigInteger
+import java.util.ArrayList
+import java.util.Arrays
+import java.util.Collections
 import java.util.List
+import java.util.regex.Pattern
 import org.opendaylight.yangtools.binding.generator.util.TypeConstants
 import org.opendaylight.yangtools.sal.binding.model.api.Constant
 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
-import java.util.ArrayList
-import java.util.Collections\rimport java.util.Arrays
 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
-import com.google.common.collect.Range
-import java.util.regex.Pattern
-import com.google.common.io.BaseEncoding
-import java.beans.ConstructorProperties
-import com.google.common.collect.Lists
 
 /**
- * Template for generating JAVA class. 
+ * Template for generating JAVA class.
  */
 class ClassTemplate extends BaseTemplate {
 
@@ -33,23 +37,22 @@ class ClassTemplate extends BaseTemplate {
     protected val List<GeneratedProperty> parentProperties
     protected val Iterable<GeneratedProperty> allProperties;
     protected val Restrictions restrictions
-    
+
     /**
      * List of enumeration which are generated as JAVA enum type.
      */
     protected val List<Enumeration> enums
-    
+
     /**
      * List of constant instances which are generated as JAVA public static final attributes.
      */
     protected val List<Constant> consts
-    
+
     /**
      * List of generated types which are enclosed inside <code>genType</code>
      */
     protected val List<GeneratedType> enclosedGeneratedTypes;
     
-    
     protected val GeneratedTransferObject genTO;
 
     /**
@@ -78,7 +81,6 @@ class ClassTemplate extends BaseTemplate {
         this.enclosedGeneratedTypes = genType.enclosedTypes
     }
 
-
     /**
      * Generates JAVA class source code (class body only).
      * 
@@ -88,7 +90,6 @@ class ClassTemplate extends BaseTemplate {
         return generateBody(true)
     }
 
-
     override protected body() {
         generateBody(false);
     }
@@ -107,9 +108,14 @@ class ClassTemplate extends BaseTemplate {
             Â«enumDeclarations»
             Â«constantsDeclarations»
             Â«generateFields»
-
-            Â«constructors»
             
+            Â«IF restrictions != null && (!restrictions.rangeConstraints.nullOrEmpty || 
+                !restrictions.lengthConstraints.nullOrEmpty)»
+            Â«generateConstraints»
+            
+            Â«ENDIF»
+            Â«constructors»
+
             Â«defaultInstance»
 
             Â«FOR field : properties SEPARATOR "\n"»
@@ -125,13 +131,30 @@ class ClassTemplate extends BaseTemplate {
 
             Â«generateToString(genTO.toStringIdentifiers)»
 
-            Â«generateLengthMethod("length", genTO, genTO.importedName, "_length")»
+            Â«generateLengthMethod("length", "_length")»
 
-            Â«generateRangeMethod("range", genTO.restrictions, genTO.importedName, "_range", allProperties)»
+            Â«generateRangeMethod("range", "_range")»
 
         }
     '''
 
+    def private generateLengthMethod(String methodName, String varName) '''
+        Â«IF restrictions != null && !(restrictions.lengthConstraints.empty)»
+            Â«val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
+            public static Â«List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> Â«methodName»() {
+                return Â«varName»;
+            }
+        Â«ENDIF»
+    '''
+
+    def private generateRangeMethod(String methodName, String varName) '''
+        Â«IF restrictions != null && !(restrictions.rangeConstraints.empty)»
+            Â«val returnType = allProperties.iterator.next.returnType»
+            public static Â«List.importedName»<«Range.importedName»<«returnType.importedNumber»>> Â«methodName»() {
+                return Â«varName»;
+            }
+        Â«ENDIF»
+    '''
 
     /**
      * Template method which generates inner classes inside this interface.
@@ -144,13 +167,12 @@ class ClassTemplate extends BaseTemplate {
                 Â«IF (innerClass instanceof GeneratedTransferObject)»
                     Â«val classTemplate = new ClassTemplate(innerClass as GeneratedTransferObject)»
                     Â«classTemplate.generateAsInnerClass»
-                    
+
                 Â«ENDIF»
             Â«ENDFOR»
         Â«ENDIF»
     '''
-    
-    
+
     def protected constructors() '''
         Â«IF genTO.unionType»
             Â«genUnionConstructor»
@@ -165,6 +187,55 @@ class ClassTemplate extends BaseTemplate {
         Â«ENDIF»
     '''
 
+    def private generateConstraints() '''
+        static {
+            Â«IF !restrictions.rangeConstraints.nullOrEmpty»
+            Â«generateRangeConstraints»
+            Â«ENDIF»
+            Â«IF !restrictions.lengthConstraints.nullOrEmpty»
+            Â«generateLengthConstraints»
+            Â«ENDIF»
+        }
+    '''
+
+    private def generateRangeConstraints() '''
+        Â«IF !allProperties.nullOrEmpty»
+            Â«val returnType = allProperties.iterator.next.returnType»
+            Â«IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
+                Â«rangeBody(restrictions, BigDecimal, genTO.importedName, "_range")»
+            Â«ELSE»
+                Â«rangeBody(restrictions, BigInteger, genTO.importedName, "_range")»
+            Â«ENDIF»
+        Â«ENDIF»
+    '''
+
+    private def rangeBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
+        Â«ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = Â«ImmutableList.importedName».builder();
+        Â«FOR r : restrictions.rangeConstraints»
+            builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», Â«numericValue(numberClass, r.max)»));
+        Â«ENDFOR»
+        Â«varName» = builder.build();
+    '''
+
+    private def lengthBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
+        Â«ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = Â«ImmutableList.importedName».builder();
+        Â«FOR r : restrictions.lengthConstraints»
+            builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», Â«numericValue(numberClass, r.max)»));
+        Â«ENDFOR»
+        Â«varName» = builder.build();
+    '''
+
+    private def generateLengthConstraints() '''
+        Â«IF restrictions != null && !(restrictions.lengthConstraints.empty)»
+            Â«val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
+            Â«IF numberClass.equals(typeof(BigDecimal))»
+                Â«lengthBody(restrictions, numberClass, genTO.importedName, "_length")»
+            Â«ELSE»
+                Â«lengthBody(restrictions, typeof(BigInteger), genTO.importedName, "_length")»
+            Â«ENDIF»
+        Â«ENDIF»
+    '''
+
     def protected allValuesConstructor() '''
     Â«IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
         @«ConstructorProperties.importedName»("value")
@@ -180,6 +251,7 @@ class ClassTemplate extends BaseTemplate {
             this.«p.fieldName» = Â«p.fieldName»;
         Â«ENDFOR»
     }
+    
     '''
 
     def protected genUnionConstructor() '''
@@ -296,7 +368,7 @@ class ClassTemplate extends BaseTemplate {
             ENDFOR»«
         ENDIF
     Â»'''
-    
+
     /**
      * Template method which generates JAVA enum type.
      * 
@@ -369,10 +441,10 @@ class ClassTemplate extends BaseTemplate {
             Â«val prop = getPropByName("value")»
             Â«IF prop != null»
                 Â«IF !(restrictions.lengthConstraints.empty)»
-                    private static Â«List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _length;
+                    private static final Â«List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _length;
                 Â«ENDIF»
                 Â«IF !(restrictions.rangeConstraints.empty)»
-                    private static Â«List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _range;
+                    private static final Â«List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _range;
                 Â«ENDIF»
             Â«ENDIF»
         Â«ENDIF»
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/util/ClassLoaderUtils.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/util/ClassLoaderUtils.java
deleted file mode 100644 (file)
index e898e72..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 2013 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.yangtools.concepts.util;
-
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.util.concurrent.Callable;
-import java.util.concurrent.locks.Lock;
-
-/**
- * @deprecated Use {@link org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils} instead.
- */
-@Deprecated
-public final class ClassLoaderUtils {
-
-    private ClassLoaderUtils() {
-        throw new UnsupportedOperationException("Utility class");
-    }
-
-    public static <V> V withClassLoader(final ClassLoader cls, final Callable<V> function) throws Exception {
-        return withClassLoaderAndLock(cls, null, function);
-    }
-
-    public static <V> V withClassLoaderAndLock(final ClassLoader cls, final Lock lock, final Callable<V> function) throws Exception {
-        if (cls == null) {
-            throw new IllegalArgumentException("Classloader should not be null");
-        }
-        if (function == null) {
-            throw new IllegalArgumentException("Function should not be null");
-        }
-
-        if (lock != null) {
-            lock.lock();
-        }
-        ClassLoader oldCls = Thread.currentThread().getContextClassLoader();
-        try {
-            Thread.currentThread().setContextClassLoader(cls);
-            return function.call();
-        } finally {
-            Thread.currentThread().setContextClassLoader(oldCls);
-            if (lock != null) {
-                lock.unlock();
-            }
-        }
-    }
-
-    public static ParameterizedType findParameterizedType(final Class<?> subclass, final Class<?> genericType) {
-        if(subclass == null || genericType == null) {
-            throw new IllegalArgumentException("Class was not specified.");
-        }
-        for (Type type : subclass.getGenericInterfaces()) {
-            if (type instanceof ParameterizedType && genericType.equals(((ParameterizedType) type).getRawType())) {
-                return (ParameterizedType) type;
-            }
-        }
-        return null;
-    }
-
-    public static <S,G,P> Class<P> findFirstGenericArgument(final Class<S> scannedClass, final Class<G> genericType) {
-        try {
-            return withClassLoader(scannedClass.getClassLoader(), ClassLoaderUtils.<S,G,P>findFirstGenericArgumentTask(scannedClass, genericType));
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    private static <S,G,P> Callable<Class<P>> findFirstGenericArgumentTask(final Class<S> scannedClass, final Class<G> genericType) {
-        return new Callable<Class<P>>() {
-            @Override
-            @SuppressWarnings("unchecked")
-            public Class<P> call() throws Exception {
-                final ParameterizedType augmentationGeneric = findParameterizedType(scannedClass,
-                        genericType);
-                if (augmentationGeneric == null) {
-                    return null;
-                }
-                return (Class<P>) augmentationGeneric.getActualTypeArguments()[0];
-            }
-        };
-    }
-
-    public static Type getFirstGenericParameter(final Type type) {
-        if(type instanceof ParameterizedType) {
-            return ((ParameterizedType) type).getActualTypeArguments()[0];
-        }
-        return null;
-    }
-
-}
\ No newline at end of file
index e8f271c075360830e0fb3a5045ac0b47c0022956..07cc5ae6bed889c99ae38fad922e54a3801f886f 100644 (file)
@@ -12,10 +12,16 @@ import java.util.Collections;
 import java.util.EventListener;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+
 import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 
-
+/**
+ * @deprecated Use {@link org.opendaylight.yangtools.util.ListenerRegistry} instead.
+ *
+ * @param <T>
+ */
+@Deprecated
 public class ListenerRegistry<T extends EventListener> implements Iterable<ListenerRegistration<T>> {
 
     private final ConcurrentHashMap<ListenerRegistration<? extends T>,ListenerRegistration<? extends T>> listeners;
@@ -34,7 +40,7 @@ public class ListenerRegistry<T extends EventListener> implements Iterable<Liste
         return unmodifiableView;
     }
 
-    public ListenerRegistration<T> register(T listener) {
+    public ListenerRegistration<T> register(final T listener) {
         if (listener == null) {
             throw new IllegalArgumentException("Listener should not be null.");
         }
@@ -42,28 +48,26 @@ public class ListenerRegistry<T extends EventListener> implements Iterable<Liste
         listeners.put(ret,ret);
         return ret;
     }
-    
-    public <L extends T> ListenerRegistration<L> registerWithType(L listener) {
+
+    public <L extends T> ListenerRegistration<L> registerWithType(final L listener) {
         ListenerRegistrationImpl<L> ret = new ListenerRegistrationImpl<L>(listener);
         listeners.put(ret,ret);
         return ret;
     }
-    
+
     @Override
     public java.util.Iterator<ListenerRegistration<T>> iterator() {
         return unmodifiableView.iterator();
     }
 
     @SuppressWarnings("rawtypes")
-    private void remove(ListenerRegistrationImpl registration) {
+    private void remove(final ListenerRegistrationImpl registration) {
         listeners.remove(registration);
     }
 
-    private class ListenerRegistrationImpl<P extends EventListener> //
-            extends AbstractObjectRegistration<P> //
-            implements ListenerRegistration<P> {
+    private class ListenerRegistrationImpl<P extends EventListener> extends AbstractObjectRegistration<P> implements ListenerRegistration<P> {
 
-        public ListenerRegistrationImpl(P instance) {
+        public ListenerRegistrationImpl(final P instance) {
             super(instance);
         }
 
index 684d959f39fd5d8f417cfb7ff8ae3747c2816893..00c1cf4e880e8a7799bd549fd2a14cb317354ce9 100644 (file)
      </dependency>
      <dependency>
        <groupId>org.opendaylight.yangtools</groupId>
-       <artifactId>yang-data-json</artifactId>
+       <artifactId>yang-data-composite-node</artifactId>
      </dependency>
      <dependency>
        <groupId>org.opendaylight.yangtools</groupId>
index 84d4dfe9da7db6fc2d455dc6e66aeff0bdeb44d6..7c62febf18c631084dfd0e0778a4e60e2442e800 100644 (file)
             </dependency>
             <dependency>
                 <groupId>org.opendaylight.yangtools</groupId>
-                <artifactId>yang-data-json</artifactId>
+                <artifactId>yang-data-composite-node</artifactId>
                 <version>0.6.2-SNAPSHOT</version>
             </dependency>
             <dependency>
index d0ab7fc85e167ceda45d5d9b3a2474f6989c1415..5d185efe517345ac63c832837e7f3db06dd3e7c4 100644 (file)
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>concepts</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
             <artifactId>junit</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>mockito-configuration</artifactId>
+            <scope>test</scope>
+            <version>0.6.2-SNAPSHOT</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ClassLoaderUtils.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ClassLoaderUtils.java
new file mode 100644 (file)
index 0000000..6f84ef8
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * 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.yangtools.util;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class ClassLoaderUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderUtils.class);
+
+    private ClassLoaderUtils() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    /**
+     *
+     * Runs {@link Supplier} with provided {@link ClassLoader}.
+     *
+     * Invokes supplies function and makes sure that original {@link ClassLoader}
+     * is context {@link ClassLoader} after execution.
+     *
+     * @param cls {@link ClassLoader} to be used.
+     * @param function Function to be executed.
+     * @return Result of supplier invocation.
+     *
+     */
+    public static <V> V withClassLoader(final ClassLoader cls, final Supplier<V> function) {
+        checkNotNull(cls, "Classloader should not be null");
+        checkNotNull(function, "Function should not be null");
+
+        final ClassLoader oldCls = Thread.currentThread().getContextClassLoader();
+        try {
+            Thread.currentThread().setContextClassLoader(cls);
+            return function.get();
+        } finally {
+            Thread.currentThread().setContextClassLoader(oldCls);
+        }
+    }
+
+    /**
+     *
+     * Runs {@link Callable} with provided {@link ClassLoader}.
+     *
+     * Invokes supplies function and makes sure that original {@link ClassLoader}
+     * is context {@link ClassLoader} after execution.
+     *
+     * @param cls {@link ClassLoader} to be used.
+     * @param function Function to be executed.
+     * @return Result of callable invocation.
+     *
+     */
+    public static <V> V withClassLoader(final ClassLoader cls, final Callable<V> function) throws Exception {
+        checkNotNull(cls, "Classloader should not be null");
+        checkNotNull(function, "Function should not be null");
+
+        final ClassLoader oldCls = Thread.currentThread().getContextClassLoader();
+        try {
+            Thread.currentThread().setContextClassLoader(cls);
+            return function.call();
+        } finally {
+            Thread.currentThread().setContextClassLoader(oldCls);
+        }
+    }
+
+    public static Object construct(final Constructor<? extends Object> constructor, final List<Object> objects)
+            throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+        final Object[] initargs = objects.toArray();
+        return constructor.newInstance(initargs);
+    }
+
+    /**
+     *
+     * Loads class using this supplied classloader.
+     *
+     *
+     * @param cls
+     * @param name String name of class.
+     * @return
+     * @throws ClassNotFoundException
+     */
+    public static Class<?> loadClass(final ClassLoader cls, final String name) throws ClassNotFoundException {
+        if ("byte[]".equals(name)) {
+            return byte[].class;
+        }
+        if ("char[]".equals(name)) {
+            return char[].class;
+        }
+
+        try {
+            return cls.loadClass(name);
+        } catch (ClassNotFoundException e) {
+            String[] components = name.split("\\.");
+            String potentialOuter;
+            int length = components.length;
+            if (length > 2 && (potentialOuter = components[length - 2]) != null && Character.isUpperCase(potentialOuter.charAt(0))) {
+                String outerName = Joiner.on(".").join(Arrays.asList(components).subList(0, length - 1));
+                String innerName = outerName + "$" + components[length-1];
+                return cls.loadClass(innerName);
+            } else {
+                throw e;
+            }
+        }
+    }
+
+    public static Class<?> loadClassWithTCCL(final String name) throws ClassNotFoundException {
+        return loadClass(Thread.currentThread().getContextClassLoader(), name);
+    }
+
+    public static Class<?> tryToLoadClassWithTCCL(final String fullyQualifiedName) {
+        try {
+            return loadClassWithTCCL(fullyQualifiedName);
+        } catch (ClassNotFoundException e) {
+            LOG.debug("Failed to load class {}", fullyQualifiedName, e);
+            return null;
+        }
+    }
+
+    public static <S,G,P> Class<P> findFirstGenericArgument(final Class<S> scannedClass, final Class<G> genericType) {
+        return withClassLoader(scannedClass.getClassLoader(), ClassLoaderUtils.<S,G,P>findFirstGenericArgumentTask(scannedClass, genericType));
+    }
+
+    private static <S,G,P> Supplier<Class<P>> findFirstGenericArgumentTask(final Class<S> scannedClass, final Class<G> genericType) {
+        return new Supplier<Class<P>>() {
+            @Override
+            @SuppressWarnings("unchecked")
+            public Class<P> get() {
+                final ParameterizedType augmentationGeneric = findParameterizedType(scannedClass, genericType);
+                if (augmentationGeneric != null) {
+                    return (Class<P>) augmentationGeneric.getActualTypeArguments()[0];
+                }
+                return null;
+            }
+        };
+    }
+
+    public static ParameterizedType findParameterizedType(final Class<?> subclass, final Class<?> genericType) {
+        Preconditions.checkNotNull(subclass);
+        Preconditions.checkNotNull(genericType);
+
+        for (Type type : subclass.getGenericInterfaces()) {
+            if (type instanceof ParameterizedType && genericType.equals(((ParameterizedType) type).getRawType())) {
+                return (ParameterizedType) type;
+            }
+        }
+
+        LOG.debug("Class {} does not declare interface {}", subclass, genericType);
+        return null;
+    }
+
+    public static Type getFirstGenericParameter(final Type type) {
+        if (type instanceof ParameterizedType) {
+            return ((ParameterizedType) type).getActualTypeArguments()[0];
+        }
+        return null;
+    }
+}
index 36f729fb8ff69210e475ad67f8466f3dd973f5f0..51a8d16f84ce007c3499b8f9e04e78766c310c6e 100644 (file)
@@ -12,6 +12,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.RejectedExecutionHandler;
 import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -25,11 +26,15 @@ public final class ExecutorServiceUtil {
     private static final class WaitInQueueExecutionHandler implements RejectedExecutionHandler {
         @Override
         public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) {
+            if( executor.isShutdown() ) {
+                throw new RejectedExecutionException( "Executor has been shutdown." );
+            }
+
             try {
                 executor.getQueue().put(r);
             } catch (InterruptedException e) {
-                LOG.debug("Intterupted while waiting for queue", e);
-                throw new RejectedExecutionException("Interrupted while waiting for queue", e);
+                LOG.debug("Interrupted while attempting to put to the queue", e);
+                throw new RejectedExecutionException("Interrupted while attempting to put to the queue", e);
             }
         }
     }
@@ -42,7 +47,7 @@ public final class ExecutorServiceUtil {
     }
 
     /**
-     * Create a {@link BlockingQueue} which does not allow for non-blocking addition to the queue.
+     * Creates a {@link BlockingQueue} which does not allow for non-blocking addition to the queue.
      * This is useful with {@link #waitInQueueExecutionHandler()} to turn force a
      * {@link ThreadPoolExecutor} to create as many threads as it is configured to before starting
      * to fill the queue.
@@ -50,7 +55,7 @@ public final class ExecutorServiceUtil {
      * @param delegate Backing blocking queue.
      * @return A new blocking queue backed by the delegate
      */
-    public <E> BlockingQueue<E> offerFailingBlockingQueue(final BlockingQueue<E> delegate) {
+    public static <E> BlockingQueue<E> offerFailingBlockingQueue(final BlockingQueue<E> delegate) {
         return new ForwardingBlockingQueue<E>() {
             @Override
             public boolean offer(final E o) {
@@ -65,12 +70,31 @@ public final class ExecutorServiceUtil {
     }
 
     /**
-     * Return a {@link RejectedExecutionHandler} which blocks on the {@link ThreadPoolExecutor}'s
+     * Returns a {@link RejectedExecutionHandler} which blocks on the {@link ThreadPoolExecutor}'s
      * backing queue if a new thread cannot be spawned.
      *
      * @return A shared RejectedExecutionHandler instance.
      */
-    public RejectedExecutionHandler waitInQueueExecutionHandler() {
+    public static RejectedExecutionHandler waitInQueueExecutionHandler() {
         return WAIT_IN_QUEUE_HANDLER;
     }
+
+    /**
+     * Tries to shutdown the given executor gracefully by awaiting termination for the given
+     * timeout period. If the timeout elapses before termination, the executor is forcefully
+     * shutdown.
+     */
+    public static void tryGracefulShutdown(final ExecutorService executor, long timeout,
+            TimeUnit unit ) {
+
+        executor.shutdown();
+
+        try {
+            if(!executor.awaitTermination(timeout, unit)) {
+                executor.shutdownNow();
+            }
+        } catch( InterruptedException e ) {
+            executor.shutdownNow();
+        }
+    }
 }
similarity index 97%
rename from common/concepts/src/main/java/org/opendaylight/yangtools/concepts/util/Immutables.java
rename to common/util/src/main/java/org/opendaylight/yangtools/util/Immutables.java
index cc2d0186d4a03c5e7af8eaca829c3d278dc6a2cd..e10977b6de01ecd0c2254a0fd1c263f4088e8aac 100644 (file)
@@ -5,7 +5,7 @@
  * 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.yangtools.concepts.util;
+package org.opendaylight.yangtools.util;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ListenerRegistry.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ListenerRegistry.java
new file mode 100644 (file)
index 0000000..e8b1a3d
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013 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.yangtools.util;
+
+
+import java.util.Collections;
+import java.util.EventListener;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+
+public class ListenerRegistry<T extends EventListener> implements Iterable<ListenerRegistration<T>> {
+
+    private final ConcurrentHashMap<ListenerRegistration<? extends T>,ListenerRegistration<? extends T>> listeners;
+    final Set<ListenerRegistration<T>> unmodifiableView;
+
+    @SuppressWarnings("unchecked")
+    public ListenerRegistry() {
+        listeners = new ConcurrentHashMap<>();
+        // This conversion is known to be safe.
+        @SuppressWarnings("rawtypes")
+        final Set rawSet = Collections.unmodifiableSet(listeners.keySet());
+        unmodifiableView = rawSet;
+    }
+
+    public Iterable<ListenerRegistration<T>> getListeners() {
+        return unmodifiableView;
+    }
+
+    public ListenerRegistration<T> register(T listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("Listener should not be null.");
+        }
+        ListenerRegistrationImpl<T> ret = new ListenerRegistrationImpl<T>(listener);
+        listeners.put(ret,ret);
+        return ret;
+    }
+    
+    public <L extends T> ListenerRegistration<L> registerWithType(L listener) {
+        ListenerRegistrationImpl<L> ret = new ListenerRegistrationImpl<L>(listener);
+        listeners.put(ret,ret);
+        return ret;
+    }
+    
+    @Override
+    public java.util.Iterator<ListenerRegistration<T>> iterator() {
+        return unmodifiableView.iterator();
+    }
+
+    @SuppressWarnings("rawtypes")
+    private void remove(ListenerRegistrationImpl registration) {
+        listeners.remove(registration);
+    }
+
+    private class ListenerRegistrationImpl<P extends EventListener> //
+            extends AbstractObjectRegistration<P> //
+            implements ListenerRegistration<P> {
+
+        public ListenerRegistrationImpl(P instance) {
+            super(instance);
+        }
+
+        @Override
+        protected void removeRegistration() {
+            ListenerRegistry.this.remove(this);
+        }
+    }
+
+    public static <T extends EventListener> ListenerRegistry<T> create() {
+        return new ListenerRegistry<>();
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/PropertyUtils.java b/common/util/src/main/java/org/opendaylight/yangtools/util/PropertyUtils.java
new file mode 100644 (file)
index 0000000..cc8f94d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.yangtools.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Strings;
+
+/**
+ * Provides utilities for system properties.
+ *
+ * @author Thomas Pantelis
+ */
+public final class PropertyUtils {
+
+    private static final Logger LOG = LoggerFactory.getLogger(PropertyUtils.class);
+
+    private PropertyUtils() {
+    }
+
+    /**
+     * Obtains the given property from the System properties and returns as an int. If the property
+     * is not found the specified default value is returned. If the property value can't be parsed
+     * to an int, a warning is logged and the default value is returned.
+     *
+     * @param propName the name of the property to get
+     * @param defaultValue the default value
+     * @return the System property as an int or the <code>defaultValue</code> if not found.
+     */
+    public static int getIntSystemProperty( String propName, int defaultValue ) {
+        int propValue = defaultValue;
+        String strValue = System.getProperty(propName);
+        if (!Strings.isNullOrEmpty(strValue) && !strValue.trim().isEmpty() ) {
+            try {
+                propValue = Integer.parseInt(strValue);
+            } catch (NumberFormatException e) {
+                LOG.warn("Cannot parse value {} for system property {}, using default {}",
+                         strValue, propName, defaultValue);
+            }
+        }
+
+        return propValue;
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListenableFutureTask.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListenableFutureTask.java
new file mode 100644 (file)
index 0000000..69c94f3
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.yangtools.util.concurrent;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.util.concurrent.ExecutionList;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * A {@link FutureTask} that also implements the {@link ListenableFuture} interface similar to
+ * guava's {@link ListenableFutureTask}. This class differs from ListenableFutureTask in that it
+ * allows an {@link Executor} to be specified on construction that is used to execute listener
+ * callback Runnables, registered via {@link #addListener}, asynchronously when this task completes.
+ * This is useful when you want to guarantee listener executions are off-loaded onto another thread
+ * to avoid blocking the thread that completed this task, as a common use case is to pass an
+ * executor that runs tasks in the same thread as the caller (ie MoreExecutors#sameThreadExecutor)
+ * to {@link #addListener}.
+ * <p>
+ * Note: the Executor specified on construction does not replace the Executor specified in
+ * {@link #addListener}. The latter Executor is still used however, if it is detected that the
+ * listener Runnable would execute in the thread that completed this task, the listener
+ * is executed on Executor specified on construction.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <V> the Future result value type
+ */
+public class AsyncNotifyingListenableFutureTask<V> extends FutureTask<V> implements ListenableFuture<V> {
+
+    private static final Logger LOG = LoggerFactory.getLogger( AsyncNotifyingListenableFutureTask.class );
+
+    /**
+     * ThreadLocal used to detect if the task completion thread is running the listeners.
+     */
+    private static final ThreadLocal<Boolean> ON_TASK_COMPLETION_THREAD_TL = new ThreadLocal<>();
+
+    /**
+     *  The execution list to hold our listeners.
+     */
+    private final ExecutionList executionList = new ExecutionList();
+
+    /**
+     * The executor used to run listener callbacks.
+     */
+    private final Executor listenerExecutor;
+
+    private AsyncNotifyingListenableFutureTask( Callable<V> callable, @Nullable Executor listenerExecutor ) {
+        super( callable );
+        this.listenerExecutor = listenerExecutor;
+    }
+
+    private AsyncNotifyingListenableFutureTask( Runnable runnable, @Nullable V result,
+            @Nullable Executor listenerExecutor ) {
+        super( runnable, result );
+        this.listenerExecutor = listenerExecutor;
+    }
+
+    /**
+     * Creates an {@code AsyncListenableFutureTask} that will upon running, execute the given
+     * {@code Callable}.
+     *
+     * @param callable the callable task
+     * @param listenerExecutor the executor used to run listener callbacks asynchronously.
+     *                         If null, no executor is used.
+     */
+    public static <V> AsyncNotifyingListenableFutureTask<V> create( Callable<V> callable,
+            @Nullable Executor listenerExecutor ) {
+      return new AsyncNotifyingListenableFutureTask<V>( callable, listenerExecutor );
+    }
+
+    /**
+     * Creates a {@code AsyncListenableFutureTask} that will upon running, execute the
+     * given {@code Runnable}, and arrange that {@code get} will return the
+     * given result on successful completion.
+     *
+     * @param runnable the runnable task
+     * @param result the result to return on successful completion.
+     * @param listenerExecutor the executor used to run listener callbacks asynchronously.
+     *                         If null, no executor is used.
+     */
+    public static <V> AsyncNotifyingListenableFutureTask<V> create( Runnable runnable, @Nullable V result,
+            @Nullable Executor listenerExecutor ) {
+      return new AsyncNotifyingListenableFutureTask<V>( runnable, result, listenerExecutor );
+    }
+
+    @Override
+    public void addListener( Runnable listener, Executor executor ) {
+        // If a listenerExecutor was specified on construction, wrap the listener Runnable in a
+        // DelegatingRunnable. If the specified executor is one that runs tasks in the same thread
+        // as the caller submitting the task (eg MoreExecutors#sameThreadExecutor) and the
+        // listener is executed from the #done method, then the DelegatingRunnable will detect this
+        // via the ThreadLocal and submit the listener Runnable to the listenerExecutor.
+        //
+        // On the other hand, if this task is already complete, the call to ExecutionList#add below
+        // will execute the listener Runnable immediately and, since the ThreadLocal won't be set,
+        // the DelegatingRunnable will run the listener Runnable inline.
+
+        executionList.add( listenerExecutor == null ? listener :
+            new DelegatingRunnable( listener, listenerExecutor ), executor );
+    }
+
+    /**
+     * Called by the base class when the future result is set. We invoke our listeners.
+     */
+    @Override
+    protected void done() {
+        ON_TASK_COMPLETION_THREAD_TL.set( Boolean.TRUE );
+        try {
+            executionList.execute();
+        } finally {
+            ON_TASK_COMPLETION_THREAD_TL.remove();
+        }
+    }
+
+    private static class DelegatingRunnable implements Runnable {
+
+        private final Runnable delegate;
+        private final Executor executor;
+
+        DelegatingRunnable( Runnable delegate, Executor executor ) {
+            this.delegate = delegate;
+            this.executor = executor;
+        }
+
+        @Override
+        public void run() {
+            if( ON_TASK_COMPLETION_THREAD_TL.get() == null ) {
+                // We're not running on the task completion thread so run the delegate inline.
+                LOG.trace( "Executing ListenenableFuture Runnable on this thread: {}",
+                        Thread.currentThread().getName() );
+                delegate.run();
+            } else {
+                // We're running on the task completion thread so off-load to the executor.
+                LOG.trace( "Submitting ListenenableFuture Runnable to the listenerExecutor",
+                        Thread.currentThread().getName() );
+                executor.execute( delegate );
+            }
+        }
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListeningExecutorService.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListeningExecutorService.java
new file mode 100644 (file)
index 0000000..ef4670d
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.yangtools.util.concurrent;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.AbstractListeningExecutorService;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * An {@link ListeningExecutorService} implementation that also allows for an {@link Executor} to be
+ * specified on construction that is used to execute {@link ListenableFuture} callback Runnables,
+ * registered via {@link Futures#addCallback} or {@link ListenableFuture#addListener} directly,
+ * asynchronously when a task that is run on this executor completes. This is useful when you want
+ * to guarantee listener callback executions are off-loaded onto another thread to avoid blocking
+ * the thread that completed the task, as a common use case is to pass an executor that runs tasks
+ * in the same thread as the caller (ie <code>MoreExecutors#sameThreadExecutor</code>}) to
+ * {@link ListenableFuture#addListener}.
+ * <p>
+ * Most commonly, this class would be used in lieu of <code>MoreExecutors#listeningDecorator<code>
+ * when the underlying delegate Executor is single-threaded, in which case, you may not want
+ * ListenableFuture callbacks to block the single thread.
+ * <p>
+ * Note: the Executor specified on construction does not replace the Executor specified in
+ * {@link ListenableFuture#addListener}. The latter Executor is still used however, if it is
+ * detected that the listener Runnable would execute in the thread that completed the task, the
+ * listener is executed on Executor specified on construction.
+ *
+ * @author Thomas Pantelis
+ * @see AsyncNotifyingListenableFutureTask
+ */
+public class AsyncNotifyingListeningExecutorService extends AbstractListeningExecutorService {
+
+    private final ExecutorService delegate;
+    private final Executor listenableFutureExecutor;
+
+    /**
+     * Constructor.
+     *
+     * @param delegate the back-end ExecutorService.
+     * @param listenableFutureExecutor the executor used to run listener callbacks asynchronously.
+     *     If null, no executor is used.
+     */
+    public AsyncNotifyingListeningExecutorService( ExecutorService delegate,
+            @Nullable Executor listenableFutureExecutor ) {
+        this.delegate = Preconditions.checkNotNull( delegate );
+        this.listenableFutureExecutor = listenableFutureExecutor;
+    }
+
+    /**
+     * Creates an {@link AsyncNotifyingListenableFutureTask} instance with the listener Executor.
+     *
+     * @param task the Callable to execute
+     */
+    private <T> AsyncNotifyingListenableFutureTask<T> newFutureTask( Callable<T> task ) {
+        return AsyncNotifyingListenableFutureTask.create( task, listenableFutureExecutor );
+    }
+
+    /**
+     * Creates an {@link AsyncNotifyingListenableFutureTask} instance with the listener Executor.
+     *
+     * @param task the Runnable to execute
+     */
+    private <T> AsyncNotifyingListenableFutureTask<T> newFutureTask( Runnable task, T result ) {
+        return AsyncNotifyingListenableFutureTask.create( task, result, listenableFutureExecutor );
+    }
+
+    /**
+     * Returns the delegate ExecutorService.
+     */
+    protected ExecutorService getDelegate() {
+        return delegate;
+    }
+
+    @Override
+    public boolean awaitTermination( long timeout, TimeUnit unit ) throws InterruptedException {
+        return delegate.awaitTermination( timeout, unit );
+    }
+
+    @Override
+    public boolean isShutdown() {
+        return delegate.isShutdown();
+    }
+
+    @Override
+    public boolean isTerminated() {
+        return delegate.isTerminated();
+    }
+
+    @Override
+    public void shutdown() {
+        delegate.shutdown();
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+        return delegate.shutdownNow();
+    }
+
+    @Override
+    public void execute( Runnable command ) {
+        delegate.execute( command );
+    }
+
+    @Override
+    public <T> ListenableFuture<T> submit( Callable<T> task ) {
+        AsyncNotifyingListenableFutureTask<T> futureTask = newFutureTask( task );
+        delegate.execute( futureTask );
+        return futureTask;
+    }
+
+    @Override
+    public ListenableFuture<?> submit( Runnable task ) {
+        AsyncNotifyingListenableFutureTask<Void> futureTask = newFutureTask( task, null );
+        delegate.execute( futureTask );
+        return futureTask;
+    }
+
+    @Override
+    public <T> ListenableFuture<T> submit( Runnable task, T result ) {
+        AsyncNotifyingListenableFutureTask<T> futureTask = newFutureTask( task, result );
+        delegate.execute( futureTask );
+        return futureTask;
+    }
+
+    protected ToStringHelper addToStringAttributes( ToStringHelper toStringHelper ) {
+        return toStringHelper;
+    }
+
+    @Override
+    public final String toString(){
+        return addToStringAttributes( Objects.toStringHelper( this )
+                .add( "delegate", delegate ) ).toString();
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CachedThreadPoolExecutor.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CachedThreadPoolExecutor.java
new file mode 100644 (file)
index 0000000..4936efa
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.yangtools.util.concurrent;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+/**
+ * A ThreadPoolExecutor with a specified bounded queue capacity that favors reusing previously
+ * constructed threads, when they are available, over creating new threads.
+ * <p>
+ * See {@link SpecialExecutors#newBoundedCachedThreadPool} for more details.
+ *
+ * @author Thomas Pantelis
+ */
+public class CachedThreadPoolExecutor extends ThreadPoolExecutor {
+
+    private static final long IDLE_TIMEOUT_IN_SEC = 60L;
+
+    private final AtomicLong largestBackingQueueSize = new AtomicLong( 0 );
+
+    private final ExecutorQueue executorQueue;
+
+    private final String threadPrefix;
+
+    private final int maximumQueueSize;
+
+    private final RejectedTaskHandler rejectedTaskHandler;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param maximumPoolSize
+     *            the maximum number of threads to allow in the pool. Threads will terminate after
+     *            being idle for 60 seconds.
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param threadPrefix
+     *            the name prefix for threads created by this executor.
+     */
+    public CachedThreadPoolExecutor( int maximumPoolSize, int maximumQueueSize, String threadPrefix ) {
+        // We're using a custom SynchronousQueue that has a backing bounded LinkedBlockingQueue.
+        // We don't specify any core threads (first parameter) so, when a task is submitted,
+        // the base class will always try to offer to the queue. If there is an existing waiting
+        // thread, the offer will succeed and the task will be handed to the thread to execute. If
+        // there's no waiting thread, either because there are no threads in the pool or all threads
+        // are busy, the base class will try to create a new thread. If the maximum thread limit has
+        // been reached, the task will be rejected. We specify a RejectedTaskHandler that tries
+        // to offer to the backing queue. If that succeeds, the task will execute as soon as a
+        // thread becomes available. If the offer fails to the backing queue, the task is rejected.
+        super( 0, maximumPoolSize, IDLE_TIMEOUT_IN_SEC, TimeUnit.SECONDS,
+               new ExecutorQueue( maximumQueueSize ) );
+
+        this.threadPrefix = Preconditions.checkNotNull( threadPrefix );
+        this.maximumQueueSize = maximumQueueSize;
+
+        setThreadFactory( new ThreadFactoryBuilder().setDaemon( true )
+                                            .setNameFormat( this.threadPrefix + "-%d" ).build() );
+
+        executorQueue = (ExecutorQueue)super.getQueue();
+
+        rejectedTaskHandler = new RejectedTaskHandler(
+                executorQueue.getBackingQueue(), largestBackingQueueSize );
+        super.setRejectedExecutionHandler( rejectedTaskHandler );
+    }
+
+    @Override
+    public void setRejectedExecutionHandler( RejectedExecutionHandler handler ) {
+        rejectedTaskHandler.setDelegateRejectedExecutionHandler( handler );
+    }
+
+    @Override
+    public BlockingQueue<Runnable> getQueue(){
+        return executorQueue.getBackingQueue();
+    }
+
+    public long getLargestQueueSize() {
+        return largestBackingQueueSize.get();
+    }
+
+    protected ToStringHelper addToStringAttributes( ToStringHelper toStringHelper ) {
+        return toStringHelper;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes( Objects.toStringHelper( this )
+                .add( "Thread Prefix", threadPrefix )
+                .add( "Current Thread Pool Size", getPoolSize() )
+                .add( "Largest Thread Pool Size", getLargestPoolSize() )
+                .add( "Max Thread Pool Size", getMaximumPoolSize() )
+                .add( "Current Queue Size", executorQueue.getBackingQueue().size() )
+                .add( "Largest Queue Size", getLargestQueueSize() )
+                .add( "Max Queue Size", maximumQueueSize )
+                .add( "Active Thread Count", getActiveCount() )
+                .add( "Completed Task Count", getCompletedTaskCount() )
+                .add( "Total Task Count", getTaskCount() ) ).toString();
+    }
+
+    /**
+     * A customized SynchronousQueue that has a backing bounded LinkedBlockingQueue. This class
+     * overrides the #poll methods to first try to poll the backing queue for a task. If the backing
+     * queue is empty, it calls the base SynchronousQueue#poll method. In this manner, we get the
+     * thread reuse behavior of the SynchronousQueue with the added ability to queue tasks when all
+     * threads are busy.
+     */
+    private static class ExecutorQueue extends SynchronousQueue<Runnable> {
+
+        private static final long serialVersionUID = 1L;
+
+        private static final long POLL_WAIT_TIME_IN_MS = 300;
+
+        private final LinkedBlockingQueue<Runnable> backingQueue;
+
+        ExecutorQueue( int maxBackingQueueSize ) {
+            backingQueue = new LinkedBlockingQueue<>( maxBackingQueueSize );
+        }
+
+        LinkedBlockingQueue<Runnable> getBackingQueue() {
+            return backingQueue;
+        }
+
+        @Override
+        public Runnable poll( long timeout, TimeUnit unit ) throws InterruptedException {
+            long totalWaitTime = unit.toMillis( timeout );
+            long waitTime = Math.min( totalWaitTime, POLL_WAIT_TIME_IN_MS );
+            Runnable task = null;
+
+            // We loop here, each time polling the backingQueue first then our queue, instead of
+            // polling each just once. This is to handle the following timing edge case:
+            //
+            //   We poll the backingQueue and it's empty but, before the call to super.poll,
+            //   a task is offered but no thread is immediately available and the task is put on the
+            //   backingQueue. There is a slight chance that all the other threads could be at the
+            //   same point, in which case they would all call super.poll and wait. If we only
+            //   called poll once, no thread would execute the task (unless/until another task was
+            //   later submitted). But by looping and breaking the specified timeout into small
+            //   periods, one thread will eventually wake up and get the task from the backingQueue
+            //   and execute it, although slightly delayed.
+
+            while( task == null ) {
+                // First try to get a task from the backing queue.
+                task = backingQueue.poll();
+                if( task == null ) {
+                    // No task in backing - call the base class to wait for one to be offered.
+                    task = super.poll( waitTime, TimeUnit.MILLISECONDS );
+
+                    totalWaitTime -= POLL_WAIT_TIME_IN_MS;
+                    if( totalWaitTime <= 0 ) {
+                        break;
+                    }
+
+                    waitTime = Math.min( totalWaitTime, POLL_WAIT_TIME_IN_MS );
+                }
+            }
+
+            return task;
+        }
+
+        @Override
+        public Runnable poll() {
+            Runnable task = backingQueue.poll();
+            return task != null ? task : super.poll();
+        }
+    }
+
+    /**
+     * Internal RejectedExecutionHandler that tries to offer rejected tasks to the backing queue.
+     * If the queue is full, we throw a RejectedExecutionException by default. The client can
+     * override this behavior be specifying their own RejectedExecutionHandler, in which case we
+     * delegate to that handler.
+     */
+    private static class RejectedTaskHandler implements RejectedExecutionHandler {
+
+        private final LinkedBlockingQueue<Runnable> backingQueue;
+        private final AtomicLong largestBackingQueueSize;
+        private volatile RejectedExecutionHandler delegateRejectedExecutionHandler;
+
+        RejectedTaskHandler( LinkedBlockingQueue<Runnable> backingQueue,
+                             AtomicLong largestBackingQueueSize ) {
+            this.backingQueue = backingQueue;
+            this.largestBackingQueueSize = largestBackingQueueSize;
+        }
+
+        void setDelegateRejectedExecutionHandler(
+                RejectedExecutionHandler delegateRejectedExecutionHandler ){
+            this.delegateRejectedExecutionHandler = delegateRejectedExecutionHandler;
+        }
+
+        @Override
+        public void rejectedExecution( Runnable task, ThreadPoolExecutor executor ) {
+            if( executor.isShutdown() ) {
+                throw new RejectedExecutionException( "Executor has been shutdown." );
+            }
+
+            if( !backingQueue.offer( task ) ) {
+                if( delegateRejectedExecutionHandler != null ) {
+                    delegateRejectedExecutionHandler.rejectedExecution( task, executor );
+                } else {
+                    throw new RejectedExecutionException(
+                                                "All threads are in use and the queue is full" );
+                }
+            }
+
+            largestBackingQueueSize.incrementAndGet();
+            long size = backingQueue.size();
+            long largest = largestBackingQueueSize.get();
+            if( size > largest ) {
+                largestBackingQueueSize.compareAndSet( largest, size );
+            }
+        }
+    }
+}
index 39332be85e092b9c5567316a2e5ac0eb84a07ddb..011872d6b138d9edbaf28869524121bd859f0b43 100644 (file)
@@ -11,18 +11,17 @@ package org.opendaylight.yangtools.util.concurrent;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.base.Function;
-import com.google.common.util.concurrent.AbstractListeningExecutorService;
 import com.google.common.util.concurrent.ForwardingListenableFuture;
 import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListenableFutureTask;
-
-import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import javax.annotation.Nullable;
+
 /**
  * An implementation of ListeningExecutorService that attempts to detect deadlock scenarios that
  * could occur if clients invoke the returned Future's <ode>get</code> methods synchronously.
@@ -45,10 +44,9 @@ import java.util.concurrent.TimeoutException;
  *
  * @author Thomas Pantelis
  */
-public class DeadlockDetectingListeningExecutorService extends AbstractListeningExecutorService {
+public class DeadlockDetectingListeningExecutorService extends AsyncNotifyingListeningExecutorService {
     private final ThreadLocal<Boolean> deadlockDetector = new ThreadLocal<>();
     private final Function<Void, Exception> deadlockExceptionFunction;
-    private final ExecutorService delegate;
 
     /**
      * Constructor.
@@ -57,61 +55,45 @@ public class DeadlockDetectingListeningExecutorService extends AbstractListening
      * @param deadlockExceptionFunction Function that returns an Exception instance to set as the
      *             cause of the ExecutionException when a deadlock is detected.
      */
-    public DeadlockDetectingListeningExecutorService(final ExecutorService delegate,
-            final Function<Void,Exception> deadlockExceptionFunction) {
-        this.delegate = checkNotNull(delegate);
-        this.deadlockExceptionFunction = checkNotNull(deadlockExceptionFunction);
-    }
-
-    @Override
-    public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException {
-        return delegate.awaitTermination(timeout, unit);
-    }
-
-    @Override
-    public boolean isShutdown() {
-        return delegate.isShutdown();
-    }
-
-    @Override
-    public boolean isTerminated() {
-        return delegate.isTerminated();
+    public DeadlockDetectingListeningExecutorService( ExecutorService delegate,
+                                          Function<Void,Exception> deadlockExceptionFunction ) {
+        this(delegate, deadlockExceptionFunction, null);
     }
 
-    @Override
-    public void shutdown() {
-        delegate.shutdown();
-    }
-
-    @Override
-    public List<Runnable> shutdownNow() {
-        return delegate.shutdownNow();
+    /**
+     * Constructor.
+     *
+     * @param delegate the backing ExecutorService.
+     * @param deadlockExceptionFunction Function that returns an Exception instance to set as the
+     *             cause of the ExecutionException when a deadlock is detected.
+     * @param listenableFutureExecutor the executor used to run listener callbacks asynchronously.
+     *             If null, no executor is used.
+     */
+    public DeadlockDetectingListeningExecutorService( ExecutorService delegate,
+                                          Function<Void,Exception> deadlockExceptionFunction,
+                                          @Nullable Executor listenableFutureExecutor ) {
+        super(delegate, listenableFutureExecutor);
+        this.deadlockExceptionFunction = checkNotNull(deadlockExceptionFunction);
     }
 
     @Override
-    public void execute(final Runnable command) {
-        delegate.execute(wrapRunnable(command));
+    public void execute( Runnable command ){
+        getDelegate().execute(wrapRunnable(command));
     }
 
     @Override
-    public <T> ListenableFuture<T> submit(final Callable<T> task ) {
-        final ListenableFutureTask<T> futureTask = ListenableFutureTask.create(wrapCallable(task));
-        delegate.execute(futureTask);
-        return wrapListenableFuture(futureTask);
+    public <T> ListenableFuture<T> submit( Callable<T> task ){
+        return wrapListenableFuture(super.submit(wrapCallable(task)));
     }
 
     @Override
-    public ListenableFuture<?> submit( final Runnable task ) {
-        ListenableFutureTask<Void> futureTask = ListenableFutureTask.create(wrapRunnable(task), null);
-        delegate.execute(futureTask);
-        return wrapListenableFuture(futureTask);
+    public ListenableFuture<?> submit( Runnable task ){
+        return wrapListenableFuture(super.submit(wrapRunnable(task)));
     }
 
     @Override
-    public <T> ListenableFuture<T> submit(final Runnable task, final T result) {
-        ListenableFutureTask<T> futureTask = ListenableFutureTask.create(wrapRunnable(task), result);
-        delegate.execute(futureTask);
-        return wrapListenableFuture(futureTask);
+    public <T> ListenableFuture<T> submit( Runnable task, T result ){
+        return wrapListenableFuture(super.submit(wrapRunnable(task), result));
     }
 
     private Runnable wrapRunnable(final Runnable task) {
@@ -122,7 +104,7 @@ public class DeadlockDetectingListeningExecutorService extends AbstractListening
                 try {
                     task.run();
                 } finally {
-                    deadlockDetector.set(null);
+                    deadlockDetector.remove();
                 }
             }
         };
@@ -136,7 +118,7 @@ public class DeadlockDetectingListeningExecutorService extends AbstractListening
                 try {
                     return delagate.call();
                 } finally {
-                    deadlockDetector.set(null);
+                    deadlockDetector.remove();
                 }
             }
         };
@@ -144,11 +126,12 @@ public class DeadlockDetectingListeningExecutorService extends AbstractListening
 
     private <T> ListenableFuture<T> wrapListenableFuture(final ListenableFuture<T> delegate ) {
         /*
-         *  This creates a forwarding Future that overrides calls to get(...) to check, via the ThreadLocal,
-         * if the caller is doing a blocking call on a thread from this executor. If so, we detect this as
-         * a deadlock and throw an ExecutionException even though it may not be a deadlock if there are
-         * more than 1 thread in the pool. Either way, there's bad practice somewhere, either on the client
-         * side for doing a blocking call or in the framework's threading model.
+         * This creates a forwarding Future that overrides calls to get(...) to check, via the
+         * ThreadLocal, if the caller is doing a blocking call on a thread from this executor. If
+         * so, we detect this as a deadlock and throw an ExecutionException even though it may not
+         * be a deadlock if there are more than 1 thread in the pool. Either way, there's bad
+         * practice somewhere, either on the client side for doing a blocking call or in the
+         * framework's threading model.
          */
         return new ForwardingListenableFuture.SimpleForwardingListenableFuture<T>(delegate) {
             @Override
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/FastThreadPoolExecutor.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/FastThreadPoolExecutor.java
new file mode 100644 (file)
index 0000000..b7549eb
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.yangtools.util.concurrent;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+/**
+ * A ThreadPoolExecutor with a specified bounded queue capacity that favors creating new threads
+ * over queuing, as the former is faster.
+ * <p>
+ * See {@link SpecialExecutors#newFastBlockingThreadPool} for more details.
+ *
+ * @author Thomas Pantelis
+ */
+public class FastThreadPoolExecutor extends ThreadPoolExecutor {
+
+    private static final long DEFAULT_IDLE_TIMEOUT_IN_SEC = 15L;
+
+    private final String threadPrefix;
+    private final int maximumQueueSize;
+
+    /**
+     * Constructs a FastThreadPoolExecutor instance.
+     *
+     * @param maximumPoolSize
+     *            the maximum number of threads to allow in the pool. Threads will terminate after
+     *            being idle for 15 seconds.
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param threadPrefix
+     *            the name prefix for threads created by this executor.
+     */
+    public FastThreadPoolExecutor( int maximumPoolSize, int maximumQueueSize, String threadPrefix ) {
+        this( maximumPoolSize, maximumQueueSize, DEFAULT_IDLE_TIMEOUT_IN_SEC, TimeUnit.SECONDS,
+              threadPrefix );
+    }
+
+    /**
+     * Constructs a FastThreadPoolExecutor instance.
+     *
+     * @param maximumPoolSize
+     *            the maximum number of threads to allow in the pool.
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param keepAliveTime
+     *            the maximum time that idle threads will wait for new tasks before terminating.
+     * @param unit
+     *            the time unit for the keepAliveTime argument
+     * @param threadPrefix
+     *            the name prefix for threads created by this executor.
+     */
+    public FastThreadPoolExecutor( int maximumPoolSize, int maximumQueueSize, long keepAliveTime,
+            TimeUnit unit, String threadPrefix ) {
+        // We use all core threads (the first 2 parameters below equal) so, when a task is submitted,
+        // if the thread limit hasn't been reached, a new thread will be spawned to execute
+        // the task even if there is an existing idle thread in the pool. This is faster than
+        // handing the task to an existing idle thread via the queue. Once the thread limit is
+        // reached, subsequent tasks will be queued. If the queue is full, tasks will be rejected.
+
+        super( maximumPoolSize, maximumPoolSize, keepAliveTime, unit,
+               new LinkedBlockingQueue<Runnable>( maximumQueueSize ) );
+
+        this.threadPrefix = threadPrefix;
+        this.maximumQueueSize = maximumQueueSize;
+
+        setThreadFactory( new ThreadFactoryBuilder().setDaemon( true )
+                                                 .setNameFormat( threadPrefix + "-%d" ).build() );
+
+        if( keepAliveTime > 0 ) {
+            // Need to specifically configure core threads to timeout.
+            allowCoreThreadTimeOut( true );
+        }
+    }
+
+    protected ToStringHelper addToStringAttributes( ToStringHelper toStringHelper ) {
+        return toStringHelper;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes( Objects.toStringHelper( this )
+                .add( "Thread Prefix", threadPrefix )
+                .add( "Current Thread Pool Size", getPoolSize() )
+                .add( "Largest Thread Pool Size", getLargestPoolSize() )
+                .add( "Max Thread Pool Size", getMaximumPoolSize() )
+                .add( "Current Queue Size", getQueue().size() )
+                .add( "Max Queue Size", maximumQueueSize )
+                .add( "Active Thread Count", getActiveCount() )
+                .add( "Completed Task Count", getCompletedTaskCount() )
+                .add( "Total Task Count", getTaskCount() ) ).toString();
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/NotificationManager.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/NotificationManager.java
new file mode 100644 (file)
index 0000000..41cc7dc
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.yangtools.util.concurrent;
+
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * Interface for a class that manages queuing and dispatching notifications for multiple listeners.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <L> the listener type
+ * @param <N> the notification type
+ */
+public interface NotificationManager<L, N> {
+
+    /**
+     * Submits a notification to be queued and dispatched to the given listener.
+     * <p>
+     * <b>Note:</b> This method may block if the listener queue is currently full.
+     *
+     * @param listener the listener to notify
+     * @param notification the notification to dispatch
+     * @throws RejectedExecutionException if the notification can't be queued for dispatching
+     */
+    void submitNotification( L listener, N notification )
+            throws RejectedExecutionException;
+
+    /**
+     * Submits notifications to be queued and dispatched to the given listener.
+     * <p>
+     * <b>Note:</b> This method may block if the listener queue is currently full.
+     *
+     * @param listener the listener to notify
+     * @param notifications the notifications to dispatch
+     * @throws RejectedExecutionException if a notification can't be queued for dispatching
+     */
+    void submitNotifications( L listener, Iterable<N> notifications )
+            throws RejectedExecutionException;
+
+}
\ No newline at end of file
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManager.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManager.java
new file mode 100644 (file)
index 0000000..472520d
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.yangtools.util.concurrent;
+
+import java.util.Arrays;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.annotation.concurrent.GuardedBy;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * This class manages queuing and dispatching notifications for multiple listeners concurrently.
+ * Notifications are queued on a per-listener basis and dispatched serially to each listener via an
+ * {@link Executor}.
+ * <p>
+ * This class optimizes its memory footprint by only allocating and maintaining a queue and executor
+ * task for a listener when there are pending notifications. On the first notification(s), a queue
+ * is created and a task is submitted to the executor to dispatch the queue to the associated
+ * listener. Any subsequent notifications that occur before all previous notifications have been
+ * dispatched are appended to the existing queue. When all notifications have been dispatched, the
+ * queue and task are discarded.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <L> the listener type
+ * @param <N> the notification type
+ */
+public class QueuedNotificationManager<L,N> implements NotificationManager<L,N> {
+
+    /**
+     * Interface implemented by clients that does the work of invoking listeners with notifications.
+     *
+     * @author Thomas Pantelis
+     *
+     * @param <L> the listener type
+     * @param <N> the notification type
+     */
+    public interface Invoker<L,N> {
+
+        /**
+         * Called to invoke a listener with a notification.
+         *
+         * @param listener the listener to invoke
+         * @param notification the notification to send
+         */
+        void invokeListener( L listener, N notification );
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger( QueuedNotificationManager.class );
+
+    private final Executor executor;
+    private final Invoker<L,N> listenerInvoker;
+
+    private final ConcurrentMap<ListenerKey<L>,NotificationTask>
+                                                          listenerCache = new ConcurrentHashMap<>();
+
+    private final String name;
+    private final int maxQueueCapacity;
+
+    /**
+     * Constructor.
+     *
+     * @param executor the {@link Executor} to use for notification tasks
+     * @param listenerInvoker the {@link Invoker} to use for invoking listeners
+     * @param maxQueueCapacity the capacity of each listener queue
+     * @param name the name of this instance for logging info
+     */
+    public QueuedNotificationManager( Executor executor, Invoker<L,N> listenerInvoker,
+            int maxQueueCapacity, String name ) {
+        this.executor = Preconditions.checkNotNull( executor );
+        this.listenerInvoker = Preconditions.checkNotNull( listenerInvoker );
+        Preconditions.checkArgument( maxQueueCapacity > 0, "maxQueueCapacity must be > 0 " );
+        this.maxQueueCapacity = maxQueueCapacity;
+        this.name = Preconditions.checkNotNull( name );
+    }
+
+    /* (non-Javadoc)
+     * @see org.opendaylight.yangtools.util.concurrent.NotificationManager#addNotification(L, N)
+     */
+    @Override
+    public void submitNotification( final L listener, final N notification )
+            throws RejectedExecutionException {
+
+        if( notification == null ) {
+            return;
+        }
+
+        submitNotifications( listener, Arrays.asList( notification ) );
+    }
+
+    /* (non-Javadoc)
+     * @see org.opendaylight.yangtools.util.concurrent.NotificationManager#submitNotifications(L, java.util.Collection)
+     */
+    @Override
+    public void submitNotifications( final L listener, final Iterable<N> notifications )
+            throws RejectedExecutionException {
+
+        if( notifications == null || listener == null ) {
+            return;
+        }
+
+        if( LOG.isTraceEnabled() ) {
+            LOG.trace( "{}: submitNotifications for listener {}: {}",
+                       name, listener.getClass(), notifications );
+        }
+
+        ListenerKey<L> key = new ListenerKey<>( listener );
+        NotificationTask newNotificationTask = null;
+
+        // Keep looping until we are either able to add a new NotificationTask or are able to
+        // add our notifications to an existing NotificationTask. Eventually one or the other
+        // will occur.
+
+        try {
+            while( true ) {
+                NotificationTask existingTask = listenerCache.get( key );
+
+                if( existingTask == null || !existingTask.submitNotifications( notifications ) ) {
+
+                    // Either there's no existing task or we couldn't add our notifications to the
+                    // existing one because it's in the process of exiting and removing itself from
+                    // the cache. Either way try to put a new task in the cache. If we can't put
+                    // then either the existing one is still there and hasn't removed itself quite
+                    // yet or some other concurrent thread beat us to the put although this method
+                    // shouldn't be called concurrently for the same listener as that would violate
+                    // notification ordering. In any case loop back up and try again.
+
+                    if( newNotificationTask == null ) {
+                        newNotificationTask = new NotificationTask( key, notifications );
+                    }
+
+                    existingTask = listenerCache.putIfAbsent( key, newNotificationTask );
+                    if( existingTask == null ) {
+
+                        // We were able to put our new task - now submit it to the executor and
+                        // we're done. If it throws a RejectedxecutionException, let that propagate
+                        // to the caller.
+
+                        LOG.debug( "{}: Submitting NotificationTask for listener {}",
+                                   name, listener.getClass() );
+
+                        executor.execute( newNotificationTask );
+                        break;
+                    }
+                } else {
+
+                    // We were able to add our notifications to an existing task so we're done.
+
+                    break;
+                }
+            }
+        } catch( InterruptedException e ) {
+
+            // We were interrupted trying to offer to the listener's queue. Somebody's probably
+            // telling us to quit.
+
+            LOG.debug( "{}: Interrupted trying to add to {} listener's queue",
+                       name, listener.getClass() );
+        }
+
+        if( LOG.isTraceEnabled() ) {
+            LOG.trace( "{}: submitNotifications dine for listener {}",
+                       name, listener.getClass() );
+        }
+    }
+
+    /**
+     * Used as the listenerCache map key. We key by listener reference identity hashCode/equals.
+     * Since we don't know anything about the listener class implementations and we're mixing
+     * multiple listener class instances in the same map, this avoids any potential issue with an
+     * equals implementation that just blindly casts the other Object to compare instead of checking
+     * for instanceof.
+     */
+    private static class ListenerKey<L> {
+
+        private final L listener;
+
+        public ListenerKey( L listener ) {
+            this.listener = listener;
+        }
+
+        L getListener() {
+            return listener;
+        }
+
+        @Override
+        public int hashCode() {
+            return System.identityHashCode( listener );
+        }
+
+        @Override
+        public boolean equals( Object obj ) {
+            ListenerKey<?> other = (ListenerKey<?>) obj;
+            return listener == other.listener;
+        }
+    }
+
+    /**
+     * Executor task for a single listener that queues notifications and sends them serially to the
+     * listener.
+     */
+    private class NotificationTask implements Runnable {
+
+        private final BlockingQueue<N> notificationQueue;
+
+        private volatile boolean done = false;
+
+        @GuardedBy("queuingLock")
+        private boolean queuedNotifications = false;
+
+        private final Lock queuingLock = new ReentrantLock();
+
+        private final ListenerKey<L> listenerKey;
+
+        NotificationTask( ListenerKey<L> listenerKey, Iterable<N> notifications ) {
+
+            this.listenerKey = listenerKey;
+            this.notificationQueue = new LinkedBlockingQueue<>( maxQueueCapacity );
+
+            for( N notification: notifications ) {
+                this.notificationQueue.add( notification );
+            }
+        }
+
+        boolean submitNotifications( Iterable<N> notifications ) throws InterruptedException {
+
+            queuingLock.lock();
+            try {
+
+                // Check the done flag - if true then #run is in the process of exiting so return
+                // false to indicate such. Otherwise, offer the notifications to the queue.
+
+                if( done ) {
+                    return false;
+                }
+
+                for( N notification: notifications ) {
+
+                    while( true ) {
+
+                        // Try to offer for up to a minute and log a message if it times out.
+
+                        // FIXME: we loop forever to guarantee delivery however this leaves it open
+                        // for 1 rogue listener to bring everyone to a halt. Another option is to
+                        // limit the tries and give up after a while and drop the notification.
+                        // Given a reasonably large queue capacity and long timeout, if we still
+                        // can't queue then most likely the listener is an unrecoverable state
+                        // (deadlock or endless loop).
+
+                        if( LOG.isDebugEnabled() ) {
+                            LOG.debug( "{}: Offering notification to the queue for listener {}: {}",
+                                       name, listenerKey.getListener().getClass(), notification );
+                        }
+
+                        if( notificationQueue.offer( notification, 1, TimeUnit.MINUTES ) ) {
+                            break;
+                        }
+
+                        LOG.warn(
+                            "{}: Timed out trying to offer a notification to the queue for listener {}." +
+                            "The queue has reached its capacity of {}",
+                            name, listenerKey.getListener().getClass(), maxQueueCapacity );
+                    }
+                }
+
+                // Set the queuedNotifications flag to tell #run that we've just queued
+                // notifications and not to exit yet, even if it thinks the queue is empty at this
+                // point.
+
+                queuedNotifications = true;
+
+            } finally {
+                queuingLock.unlock();
+            }
+
+            return true;
+        }
+
+        @Override
+        public void run() {
+
+            try {
+                // Loop until we've dispatched all the notifications in the queue.
+
+                while( true ) {
+
+                    // Get the notification at the head of the queue, waiting a little bit for one
+                    // to get offered.
+
+                    N notification = notificationQueue.poll( 10, TimeUnit.MILLISECONDS );
+                    if( notification == null ) {
+
+                        // The queue is empty - try to get the queuingLock. If we can't get the lock
+                        // then #submitNotifications is in the process of offering to the queue so
+                        // we'll loop back up and poll the queue again.
+
+                        if( queuingLock.tryLock() ) {
+                            try {
+
+                                // Check the queuedNotifications flag to see if #submitNotifications
+                                // has offered new notification(s) to the queue. If so, loop back up
+                                // and poll the queue again. Otherwise set done to true and exit.
+                                // Once we set the done flag and unlock, calls to
+                                // #submitNotifications will fail and a new task will be created.
+
+                                if( !queuedNotifications ) {
+                                    done = true;
+                                    break;
+                                }
+
+                                // Clear the queuedNotifications flag so we'll try to exit the next
+                                // time through the loop when the queue is empty.
+
+                                queuedNotifications = false;
+
+                            } finally {
+                                queuingLock.unlock();
+                            }
+                        }
+                    }
+
+                    notifyListener( notification );
+                }
+            } catch( InterruptedException e ) {
+
+                // The executor is probably shutting down so log as debug.
+                LOG.debug( "{}: Interrupted trying to remove from {} listener's queue",
+                           name, listenerKey.getListener().getClass() );
+            } finally {
+
+                // We're exiting, gracefully or not - either way make sure we always remove
+                // ourselves from the cache.
+
+                listenerCache.remove( listenerKey );
+            }
+        }
+
+        private void notifyListener( N notification ) {
+
+            if( notification == null ) {
+                return;
+            }
+
+            try {
+
+                if( LOG.isDebugEnabled() ) {
+                    LOG.debug( "{}: Invoking listener {} with notification: {}",
+                               name, listenerKey.getListener().getClass(), notification );
+                }
+
+                listenerInvoker.invokeListener( listenerKey.getListener(), notification );
+
+            } catch( RuntimeException e ) {
+
+                // We'll let a RuntimeException from the listener slide and keep sending any
+                // remaining notifications.
+
+                LOG.error( String.format( "%1$s: Error notifying listener %2$s", name,
+                           listenerKey.getListener().getClass() ), e );
+
+            } catch( Error e ) {
+
+                // A JVM Error is severe - best practice is to throw them up the chain. Set done to
+                // true so no new notifications can be added to this task as we're about to bail.
+
+                done = true;
+                throw e;
+            }
+        }
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SpecialExecutors.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SpecialExecutors.java
new file mode 100644 (file)
index 0000000..0548d7a
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.yangtools.util.concurrent;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Factory methods for creating {@link ExecutorService} instances with specific configurations.
+
+ * @author Thomas Pantelis
+ */
+public final class SpecialExecutors {
+
+    private SpecialExecutors() {
+    }
+
+    /**
+     * Creates an ExecutorService with a specified bounded queue capacity that favors creating new
+     * threads over queuing, as the former is faster, so threads will only be reused when the thread
+     * limit is exceeded and tasks are queued. If the maximum queue capacity is reached, subsequent
+     * tasks will be rejected.
+     * <p>
+     * For example, if the maximum number of threads is 100 and 100 short-lived tasks are submitted
+     * within say 10 seconds, then 100 threads will be created and used - previously constructed
+     * idle threads will not be reused. This provides the fastest execution of the 100 tasks at the
+     * expense of memory and thread resource overhead. Therefore it is advisable to specify a
+     * relatively small thread limit (probably no more than 50).
+     * <p>
+     * Threads that have not been used for 15 seconds are terminated and removed from the pool.
+     * Thus, a pool that remains idle for long enough will not consume any resources.
+     * <p>
+     * If you need an executor with less memory and thread resource overhead where slower execution
+     * time is acceptable, consider using {@link #newBoundedCachedThreadPool }.
+     *
+     * @param maximumPoolSize
+     *            the maximum number of threads to allow in the pool. Threads will terminate after
+     *            being idle for 15 seconds.
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param threadPrefix
+     *            the name prefix for threads created by this executor.
+     * @return a new ExecutorService with the specified configuration.
+     */
+    public static ExecutorService newBoundedFastThreadPool( int maximumPoolSize,
+            int maximumQueueSize, String threadPrefix ) {
+        return new FastThreadPoolExecutor( maximumPoolSize, maximumQueueSize, threadPrefix );
+    }
+
+    /**
+     * Creates an ExecutorService similar to {@link #newBoundedFastThreadPool } except that it
+     * handles rejected tasks by running them in the same thread as the caller. Therefore if the
+     * queue is full, the caller submitting the task will be blocked until the task completes. In
+     * this manner, tasks are never rejected.
+     *
+     * @param maximumPoolSize
+     *            the maximum number of threads to allow in the pool. Threads will terminate after
+     *            being idle for 15 seconds.
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param threadPrefix
+     *            the name prefix for threads created by this executor.
+     * @return a new ExecutorService with the specified configuration.
+     */
+    public static ExecutorService newBlockingBoundedFastThreadPool( int maximumPoolSize,
+            int maximumQueueSize, String threadPrefix ) {
+
+        FastThreadPoolExecutor executor =
+                new FastThreadPoolExecutor( maximumPoolSize, maximumQueueSize, threadPrefix );
+        executor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy() );
+        return executor;
+    }
+
+    /**
+     * Creates an ExecutorService with a specified bounded queue capacity that favors reusing
+     * previously constructed threads, when they are available, over creating new threads. When a
+     * task is submitted, if no existing thread is available, a new thread will be created and added
+     * to the pool. If there is an existing idle thread available, the task will be handed to that
+     * thread to execute. If the specified maximum thread limit is reached, subsequent tasks will be
+     * queued and will execute as threads become available. If the maximum queue capacity is
+     * reached, subsequent tasks will be rejected.
+     * <p>
+     * Threads that have not been used for sixty seconds are terminated and removed from the pool.
+     * Thus, a pool that remains idle for long enough will not consume any resources.
+     * <p>
+     * By reusing threads when possible, this executor optimizes for reduced memory and thread
+     * resource overhead at the expense of execution time.
+     * <p>
+     * If you need an executor with faster execution time where increased memory and thread resource
+     * overhead is acceptable, consider using {@link #newBoundedFastThreadPool }.
+     *
+     * @param maximumPoolSize
+     *            the maximum number of threads to allow in the pool. Threads will terminate after
+     *            being idle for 60 seconds.
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param threadPrefix
+     *            the name prefix for threads created by this executor.
+     * @return a new ExecutorService with the specified configuration.
+     */
+    public static ExecutorService newBoundedCachedThreadPool( int maximumPoolSize,
+            int maximumQueueSize, String threadPrefix ) {
+        return new CachedThreadPoolExecutor( maximumPoolSize, maximumQueueSize, threadPrefix );
+    }
+
+    /**
+     * Creates an ExecutorService similar to {@link #newBoundedCachedThreadPool } except that it
+     * handles rejected tasks by running them in the same thread as the caller. Therefore if the
+     * queue is full, the caller submitting the task will be blocked until the task completes. In
+     * this manner, tasks are never rejected.
+     *
+     * @param maximumPoolSize
+     *            the maximum number of threads to allow in the pool. Threads will terminate after
+     *            being idle for 60 seconds.
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param threadPrefix
+     *            the name prefix for threads created by this executor.
+     * @return a new ExecutorService with the specified configuration.
+     */
+    public static ExecutorService newBlockingBoundedCachedThreadPool( int maximumPoolSize,
+            int maximumQueueSize, String threadPrefix ) {
+
+        CachedThreadPoolExecutor executor =
+                new CachedThreadPoolExecutor( maximumPoolSize, maximumQueueSize, threadPrefix );
+        executor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy() );
+        return executor;
+    }
+
+    /**
+     * Creates an ExecutorService that uses a single worker thread operating off a bounded queue
+     * with the specified capacity. Tasks are guaranteed to execute sequentially, and no more than
+     * one task will be active at any given time. If the maximum queue capacity is reached,
+     * subsequent tasks will be rejected.
+     *
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param threadPrefix
+     *            the name prefix for the thread created by this executor.
+     * @return a new ExecutorService with the specified configuration.
+     */
+    public static ExecutorService newBoundedSingleThreadExecutor( int maximumQueueSize,
+            String threadPrefix ) {
+        return new FastThreadPoolExecutor( 1, maximumQueueSize, Long.MAX_VALUE, TimeUnit.SECONDS,
+                threadPrefix );
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListeningExecutorServiceTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListeningExecutorServiceTest.java
new file mode 100644 (file)
index 0000000..1a3bf0d
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.yangtools.util.concurrent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.After;
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.Invoker;
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_CALLABLE;
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_RUNNABLE;
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_RUNNABLE_WITH_RESULT;
+
+/**
+ * Unit tests for AsyncNotifyingListeningExecutorService.
+ *
+ * @author Thomas Pantelis
+ */
+public class AsyncNotifyingListeningExecutorServiceTest {
+
+    private ExecutorService listenerExecutor;
+    private AsyncNotifyingListeningExecutorService testExecutor;
+
+    @After
+    public void tearDown() {
+        if( listenerExecutor != null ) {
+            listenerExecutor.shutdownNow();
+        }
+
+        if( testExecutor != null ) {
+            testExecutor.shutdownNow();
+        }
+    }
+
+    @Test
+    public void testListenerCallbackWithExecutor() throws InterruptedException {
+
+        String listenerThreadPrefix = "ListenerThread";
+        listenerExecutor = Executors.newFixedThreadPool( 3,
+                new ThreadFactoryBuilder().setNameFormat( listenerThreadPrefix + "-%d" ).build() );
+
+        testExecutor = new AsyncNotifyingListeningExecutorService(
+                Executors.newSingleThreadExecutor(
+                        new ThreadFactoryBuilder().setNameFormat( "SingleThread" ).build() ),
+                listenerExecutor );
+
+        testListenerCallback( testExecutor, SUBMIT_CALLABLE, listenerThreadPrefix );
+        testListenerCallback( testExecutor, SUBMIT_RUNNABLE, listenerThreadPrefix );
+        testListenerCallback( testExecutor, SUBMIT_RUNNABLE_WITH_RESULT, listenerThreadPrefix );
+    }
+
+    @Test
+    public void testListenerCallbackWithNoExecutor() throws InterruptedException {
+
+        String listenerThreadPrefix = "SingleThread";
+        testExecutor = new AsyncNotifyingListeningExecutorService(
+                Executors.newSingleThreadExecutor(
+                        new ThreadFactoryBuilder().setNameFormat( listenerThreadPrefix ).build() ),
+                null );
+
+        testListenerCallback( testExecutor, SUBMIT_CALLABLE, listenerThreadPrefix );
+        testListenerCallback( testExecutor, SUBMIT_RUNNABLE, listenerThreadPrefix );
+        testListenerCallback( testExecutor, SUBMIT_RUNNABLE_WITH_RESULT, listenerThreadPrefix );
+    }
+
+    static void testListenerCallback( AsyncNotifyingListeningExecutorService executor,
+            Invoker invoker, final String expListenerThreadPrefix ) throws InterruptedException {
+
+        AtomicReference<AssertionError> assertError = new AtomicReference<>();
+        CountDownLatch futureNotifiedLatch = new CountDownLatch( 1 );
+        CountDownLatch blockTaskLatch = new CountDownLatch( 1 );
+
+        // The blockTaskLatch is used to block the task from completing until we've added
+        // our listener to the Future. Otherwise, if the task completes quickly and the Future is
+        // set to done before we've added our listener, the call to ListenableFuture#addListener
+        // will immediately notify synchronously on this thread as Futures#addCallback defaults to
+        // a same thread executor. This would erroneously fail the test.
+
+        ListenableFuture<?> future = invoker.invokeExecutor( executor, blockTaskLatch );
+        addCallback( future, futureNotifiedLatch, expListenerThreadPrefix, assertError );
+
+        // Now that we've added our listener, signal the latch to let the task complete.
+
+        blockTaskLatch.countDown();
+
+        assertTrue( "ListenableFuture callback was not notified of onSuccess",
+                    futureNotifiedLatch.await( 5, TimeUnit.SECONDS ) );
+
+        if( assertError.get() != null ) {
+            throw assertError.get();
+        }
+
+        // Add another listener - since the Future is already complete, we expect the listener to be
+        // notified inline on this thread when it's added.
+
+        futureNotifiedLatch = new CountDownLatch( 1 );
+        addCallback( future, futureNotifiedLatch, Thread.currentThread().getName(), assertError );
+
+        assertTrue( "ListenableFuture callback was not notified of onSuccess",
+                    futureNotifiedLatch.await( 5, TimeUnit.SECONDS ) );
+
+        if( assertError.get() != null ) {
+            throw assertError.get();
+        }
+    }
+
+    static void addCallback( ListenableFuture<?> future,
+            final CountDownLatch futureNotifiedLatch,
+            final String expListenerThreadPrefix,
+            final AtomicReference<AssertionError> assertError ) {
+
+        Futures.addCallback( future, new FutureCallback<Object>() {
+            @Override
+            public void onSuccess( Object result ) {
+
+                try {
+                    String theadName = Thread.currentThread().getName();
+                    assertTrue( "ListenableFuture callback was not notified on the listener executor."
+                        + " Expected thread name prefix \"" + expListenerThreadPrefix +
+                            "\". Actual thread name \"" + theadName + "\"",
+                            theadName.startsWith( expListenerThreadPrefix ) );
+                } catch( AssertionError e ) {
+                    assertError.set( e );
+                } finally {
+                    futureNotifiedLatch.countDown();
+                }
+            }
+
+            @Override
+            public void onFailure( Throwable t ) {
+                // Shouldn't happen
+                t.printStackTrace();
+            }
+        } );
+    }
+
+    @Test
+    public void testDelegatedMethods() throws InterruptedException {
+
+        Runnable task = new Runnable() {
+            @Override
+            public void run(){
+            }
+        };
+
+        List<Runnable> taskList = Lists.newArrayList();
+
+        ExecutorService mockDelegate = mock( ExecutorService.class );
+        doNothing().when( mockDelegate ).execute( task );
+        doNothing().when( mockDelegate ).shutdown();
+        doReturn( taskList ).when( mockDelegate ).shutdownNow();
+        doReturn( true ).when( mockDelegate ).awaitTermination( 3, TimeUnit.SECONDS );
+        doReturn( true ).when( mockDelegate ).isShutdown();
+        doReturn( true ).when( mockDelegate ).isTerminated();
+
+        AsyncNotifyingListeningExecutorService executor = new AsyncNotifyingListeningExecutorService(
+                                                                   mockDelegate, null );
+
+        executor.execute( task );
+        executor.shutdown();
+        assertEquals( "awaitTermination", true, executor.awaitTermination( 3, TimeUnit.SECONDS ) );
+        assertSame( "shutdownNow", taskList, executor.shutdownNow() );
+        assertEquals( "isShutdown", true, executor.isShutdown() );
+        assertEquals( "isTerminated", true, executor.isTerminated() );
+
+        verify( mockDelegate ).execute( task );
+        verify( mockDelegate ).shutdown();
+        verify( mockDelegate ).awaitTermination( 3, TimeUnit.SECONDS );
+        verify( mockDelegate ).shutdownNow();
+        verify( mockDelegate ).isShutdown();
+        verify( mockDelegate ).isTerminated();
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/CommonTestUtils.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/CommonTestUtils.java
new file mode 100644 (file)
index 0000000..60c56a4
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.yangtools.util.concurrent;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.Uninterruptibles;
+
+/**
+ * Some common test utilities.
+ *
+ * @author Thomas Pantelis
+ */
+public class CommonTestUtils {
+
+    public interface Invoker {
+        ListenableFuture<?> invokeExecutor( ListeningExecutorService executor,
+                CountDownLatch blockingLatch );
+    };
+
+    public static final Invoker SUBMIT_CALLABLE = new Invoker() {
+        @Override
+        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor,
+                final CountDownLatch blockingLatch ) {
+            return executor.submit( new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    if( blockingLatch != null ) {
+                        Uninterruptibles.awaitUninterruptibly( blockingLatch );
+                    }
+                    return null;
+                }
+            } );
+        }
+    };
+
+    public static final Invoker SUBMIT_RUNNABLE =  new Invoker() {
+        @Override
+        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor,
+                final CountDownLatch blockingLatch ) {
+            return executor.submit( new Runnable() {
+                @Override
+                public void run() {
+                    if( blockingLatch != null ) {
+                        Uninterruptibles.awaitUninterruptibly( blockingLatch );
+                    }
+                }
+            } );
+        }
+    };
+
+    public static final Invoker SUBMIT_RUNNABLE_WITH_RESULT = new Invoker() {
+        @Override
+        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor,
+                final CountDownLatch blockingLatch ) {
+            return executor.submit( new Runnable() {
+                @Override
+                public void run() {
+                    if( blockingLatch != null ) {
+                        Uninterruptibles.awaitUninterruptibly( blockingLatch );
+                    }
+                }
+            }, "foo" );
+        }
+    };
+}
index b23750da986f94f6c08a392558a6924f50a2ef7d..6bba351dcabb28a56a7c3cf434c32f29b63adfae 100644 (file)
@@ -13,10 +13,12 @@ import static org.junit.Assert.*;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -25,6 +27,13 @@ import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import static org.opendaylight.yangtools.util.concurrent.AsyncNotifyingListeningExecutorServiceTest.testListenerCallback;
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.Invoker;
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_CALLABLE;
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_RUNNABLE;
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_RUNNABLE_WITH_RESULT;
 
 /**
  * Unit tests for DeadlockDetectingListeningExecutorService.
@@ -33,44 +42,6 @@ import com.google.common.util.concurrent.ListeningExecutorService;
  */
 public class DeadlockDetectingListeningExecutorServiceTest {
 
-    interface Invoker {
-        ListenableFuture<?> invokeExecutor( ListeningExecutorService executor );
-    };
-
-    static final Invoker SUBMIT_CALLABLE = new Invoker() {
-        @Override
-        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor ) {
-            return executor.submit( new Callable<String>() {
-                @Override
-                public String call() throws Exception{
-                    return "foo";
-                }
-            } );
-        }
-    };
-
-    static final Invoker SUBMIT_RUNNABLE =  new Invoker() {
-        @Override
-        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor ) {
-            return executor.submit( new Runnable() {
-                @Override
-                public void run(){
-                }
-            } );
-        }
-    };
-
-    static final Invoker SUBMIT_RUNNABLE_WITH_RESULT = new Invoker() {
-        @Override
-        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor ) {
-            return executor.submit( new Runnable() {
-                @Override
-                public void run(){
-                }
-            }, "foo" );
-        }
-    };
-
     interface InitialInvoker {
         void invokeExecutor( ListeningExecutorService executor, Runnable task );
     };
@@ -104,13 +75,25 @@ public class DeadlockDetectingListeningExecutorServiceTest {
 
     @Before
     public void setup() {
-        executor = new DeadlockDetectingListeningExecutorService( Executors.newSingleThreadExecutor(),
-                                                                  DEADLOCK_EXECUTOR_FUNCTION );
+    }
+
+    @After
+    public void tearDown() {
+        if( executor != null ) {
+            executor.shutdownNow();
+        }
+    }
+
+    DeadlockDetectingListeningExecutorService newExecutor() {
+        return new DeadlockDetectingListeningExecutorService( Executors.newSingleThreadExecutor(),
+                DEADLOCK_EXECUTOR_FUNCTION );
     }
 
     @Test
     public void testBlockingSubmitOffExecutor() throws Exception {
 
+        executor = newExecutor();
+
         // Test submit with Callable.
 
         ListenableFuture<String> future = executor.submit( new Callable<String>() {
@@ -144,6 +127,8 @@ public class DeadlockDetectingListeningExecutorServiceTest {
     @Test
     public void testNonBlockingSubmitOnExecutorThread() throws Throwable {
 
+        executor = newExecutor();
+
         testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_CALLABLE );
         testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE );
         testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE_WITH_RESULT );
@@ -162,7 +147,7 @@ public class DeadlockDetectingListeningExecutorServiceTest {
             @Override
             public void run() {
 
-                Futures.addCallback( invoker.invokeExecutor( executor ), new FutureCallback() {
+                Futures.addCallback( invoker.invokeExecutor( executor, null ), new FutureCallback() {
                     @Override
                     public void onSuccess( Object result ) {
                         futureCompletedLatch.countDown();
@@ -191,6 +176,8 @@ public class DeadlockDetectingListeningExecutorServiceTest {
     @Test
     public void testBlockingSubmitOnExecutorThread() throws Exception {
 
+        executor = newExecutor();
+
         testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_CALLABLE );
         testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE );
         testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE_WITH_RESULT );
@@ -209,7 +196,7 @@ public class DeadlockDetectingListeningExecutorServiceTest {
             public void run() {
 
                 try {
-                    invoker.invokeExecutor( executor ).get();
+                    invoker.invokeExecutor( executor, null ).get();
                 } catch( ExecutionException e ) {
                     caughtEx.set( e.getCause() );
                 } catch( Throwable e ) {
@@ -229,4 +216,25 @@ public class DeadlockDetectingListeningExecutorServiceTest {
         assertNotNull( "Expected exception thrown", caughtEx.get() );
         assertEquals( "Caught exception type", TestDeadlockException.class, caughtEx.get().getClass() );
     }
+
+    @Test
+    public void testListenableFutureCallbackWithExecutor() throws InterruptedException {
+
+        String listenerThreadPrefix = "ListenerThread";
+        ExecutorService listenerExecutor = Executors.newFixedThreadPool( 1,
+                new ThreadFactoryBuilder().setNameFormat( listenerThreadPrefix + "-%d" ).build() );
+
+        executor = new DeadlockDetectingListeningExecutorService(
+                Executors.newSingleThreadExecutor(
+                        new ThreadFactoryBuilder().setNameFormat( "SingleThread" ).build() ),
+                DEADLOCK_EXECUTOR_FUNCTION, listenerExecutor );
+
+        try {
+            testListenerCallback( executor, SUBMIT_CALLABLE, listenerThreadPrefix );
+            testListenerCallback( executor, SUBMIT_RUNNABLE, listenerThreadPrefix );
+            testListenerCallback( executor, SUBMIT_RUNNABLE_WITH_RESULT, listenerThreadPrefix );
+        } finally {
+            listenerExecutor.shutdownNow();
+        }
+    }
 }
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManagerTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManagerTest.java
new file mode 100644 (file)
index 0000000..d7e0e50
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.yangtools.util.concurrent;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.Test;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Uninterruptibles;
+
+/**
+ * Unit tests for QueuedNotificationManager.
+ *
+ * @author Thomas Pantelis
+ */
+public class QueuedNotificationManagerTest {
+
+    static class TestListener<N> {
+
+        private final List<N> actual;
+        private volatile int expCount;
+        private volatile CountDownLatch latch;
+        volatile long sleepTime = 0;
+        volatile RuntimeException runtimeEx;
+        volatile Error jvmError;
+        boolean cacheNotifications = true;
+        String name;
+
+        TestListener( int expCount, int id ) {
+            name = "TestListener " + id;
+            actual = Collections.synchronizedList( Lists.<N>newArrayListWithCapacity( expCount ) );
+            reset( expCount );
+        }
+
+        void reset( int expCount ) {
+            this.expCount = expCount;
+            latch = new CountDownLatch( expCount );
+            actual.clear();
+        }
+
+        void onNotification( N data ) {
+
+            try {
+                if( sleepTime > 0 ) {
+                    Uninterruptibles.sleepUninterruptibly( sleepTime, TimeUnit.MILLISECONDS );
+                }
+
+                if( cacheNotifications ) {
+                    actual.add( data );
+                }
+
+                RuntimeException localRuntimeEx = runtimeEx;
+                if( localRuntimeEx != null ) {
+                    runtimeEx = null;
+                    throw localRuntimeEx;
+                }
+
+                Error localJvmError = jvmError;
+                if( localJvmError != null ) {
+                    jvmError = null;
+                    throw localJvmError;
+                }
+
+            } finally {
+                latch.countDown();
+            }
+        }
+
+        void verifyNotifications() {
+            boolean done = Uninterruptibles.awaitUninterruptibly( latch, 10, TimeUnit.SECONDS );
+            if( !done ) {
+                long actualCount = latch.getCount();
+                fail( name + ": Received " + (expCount - actualCount) +
+                      " notifications. Expected " + expCount );
+            }
+        }
+
+        void verifyNotifications( List<N> expected ) {
+            verifyNotifications();
+            assertEquals( name + ": Notifications", Lists.newArrayList( expected ), actual );
+        }
+
+        // Implement bad hashCode/equals methods to verify it doesn't screw up the
+        // QueuedNotificationManager as it should use reference identity.
+        @Override
+        public int hashCode(){
+            return 1;
+        }
+
+        @Override
+        public boolean equals( Object obj ){
+            TestListener<?> other = (TestListener<?>) obj;
+            return other != null;
+        }
+    }
+
+    static class TestListener2<N> extends TestListener<N> {
+        TestListener2( int expCount, int id  ) {
+            super(expCount, id);
+        }
+    }
+
+    static class TestListener3<N> extends TestListener<N> {
+        TestListener3( int expCount, int id ) {
+            super(expCount, id);
+        }
+    }
+
+    static class TestNotifier<N> implements QueuedNotificationManager.Invoker<TestListener<N>,N> {
+
+        @Override
+        public void invokeListener( TestListener<N> listener, N notification ) {
+            listener.onNotification( notification );
+        }
+    }
+
+    private ExecutorService queueExecutor;
+
+    @After
+    public void tearDown() {
+        if( queueExecutor != null ) {
+            queueExecutor.shutdownNow();
+        }
+    }
+
+    @Test(timeout=10000)
+    public void testNotificationsWithSingleListener() {
+
+        queueExecutor = Executors.newFixedThreadPool( 2 );
+        NotificationManager<TestListener<Integer>, Integer> manager =
+                new QueuedNotificationManager<>( queueExecutor, new TestNotifier<Integer>(),
+                10, "TestMgr" );
+
+        int initialCount = 6;
+        int nNotifications = 100;
+
+        TestListener<Integer> listener = new TestListener<>( nNotifications, 1 );
+        listener.sleepTime = 20;
+
+        manager.submitNotifications( listener, Arrays.asList( 1, 2 ) );
+        manager.submitNotification( listener, 3 );
+        manager.submitNotifications( listener, Arrays.asList( 4, 5 ) );
+        manager.submitNotification( listener, 6 );
+
+        manager.submitNotifications( null, Collections.<Integer>emptyList() );
+        manager.submitNotifications( listener, null );
+        manager.submitNotification( listener, null );
+
+        Uninterruptibles.sleepUninterruptibly( 100, TimeUnit.MILLISECONDS );
+
+        listener.sleepTime = 0;
+
+        List<Integer> expNotifications = Lists.newArrayListWithCapacity( nNotifications );
+        expNotifications.addAll( Arrays.asList( 1, 2, 3, 4, 5, 6 ) );
+        for( int i = 1; i <= nNotifications - initialCount; i++ ) {
+            Integer v = Integer.valueOf( initialCount + i );
+            expNotifications.add( v );
+            manager.submitNotification( listener, v );
+        }
+
+        listener.verifyNotifications( expNotifications );
+    }
+
+    @Test
+    public void testNotificationsWithMultipleListeners() {
+
+        int nListeners = 10;
+        queueExecutor = Executors.newFixedThreadPool( nListeners );
+        final ExecutorService stagingExecutor = Executors.newFixedThreadPool( nListeners );
+        final NotificationManager<TestListener<Integer>, Integer> manager =
+                new QueuedNotificationManager<>( queueExecutor, new TestNotifier<Integer>(),
+                5000, "TestMgr" );
+
+        final int nNotifications = 100000;
+
+        System.out.println( "Testing " + nListeners + " listeners with " + nNotifications +
+                            " notifications each..." );
+
+        final Integer[] notifications = new Integer[nNotifications];
+        for( int i = 1; i <= nNotifications; i++ ) {
+            notifications[i-1] = Integer.valueOf( i );
+        }
+
+        Stopwatch stopWatch = new Stopwatch();
+        stopWatch.start();
+
+        List<TestListener<Integer>> listeners = Lists.newArrayList();
+        for( int i = 1; i <= nListeners; i++ ) {
+            final TestListener<Integer> listener =
+                    i == 2 ? new TestListener2<Integer>( nNotifications, i ) :
+                    i == 3 ? new TestListener3<Integer>( nNotifications, i ) :
+                                      new TestListener<Integer>( nNotifications, i );
+            listeners.add( listener );
+
+            new Thread( new Runnable() {
+                @Override
+                public void run() {
+                    for( int j = 1; j <= nNotifications; j++ ) {
+                        final Integer n = notifications[j-1];
+                        stagingExecutor.execute( new Runnable() {
+                            @Override
+                            public void run() {
+                                manager.submitNotification( listener, n );
+                            }
+                        } );
+                    }
+                }
+            } ).start();
+        }
+
+        try {
+            for( TestListener<Integer> listener: listeners ) {
+                listener.verifyNotifications();
+                System.out.println( listener.name + " succeeded" );
+            }
+        } finally {
+            stagingExecutor.shutdownNow();
+        }
+
+        stopWatch.stop();
+
+        System.out.println( "Elapsed time: " + stopWatch );
+        System.out.println( queueExecutor );
+    }
+
+    @Test(timeout=10000)
+    public void testNotificationsWithListenerRuntimeEx() {
+
+        queueExecutor = Executors.newFixedThreadPool( 1 );
+        NotificationManager<TestListener<Integer>, Integer> manager =
+                new QueuedNotificationManager<>( queueExecutor, new TestNotifier<Integer>(),
+                10, "TestMgr" );
+
+
+        TestListener<Integer> listener = new TestListener<>( 2, 1 );
+        listener.runtimeEx = new RuntimeException( "mock" );
+
+        manager.submitNotification( listener, 1 );
+        manager.submitNotification( listener, 2 );
+
+        listener.verifyNotifications();
+    }
+
+    @Test(timeout=10000)
+    public void testNotificationsWithListenerJVMError() {
+
+        final CountDownLatch errorCaughtLatch = new CountDownLatch( 1 );
+        queueExecutor = new ThreadPoolExecutor( 1, 1, 0, TimeUnit.SECONDS,
+                                                new LinkedBlockingQueue<Runnable>() ) {
+             @Override
+             public void execute( final Runnable command ) {
+                 super.execute( new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            command.run();
+                        } catch( Error e ) {
+                            errorCaughtLatch.countDown();
+                        }
+                    }
+                });
+             }
+        };
+
+        NotificationManager<TestListener<Integer>, Integer> manager =
+                new QueuedNotificationManager<>( queueExecutor, new TestNotifier<Integer>(),
+                10, "TestMgr" );
+
+        TestListener<Integer> listener = new TestListener<>( 2, 1 );
+        listener.jvmError = new Error( "mock" );
+
+        manager.submitNotification( listener, 1 );
+
+        assertEquals( "JVM Error caught", true, Uninterruptibles.awaitUninterruptibly(
+                                                       errorCaughtLatch, 5, TimeUnit.SECONDS ) );
+
+        manager.submitNotification( listener, 2 );
+
+        listener.verifyNotifications();
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ThreadPoolExecutorTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ThreadPoolExecutorTest.java
new file mode 100644 (file)
index 0000000..8270e45
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.yangtools.util.concurrent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.After;
+import org.junit.Test;
+
+import com.google.common.base.Stopwatch;
+
+/**
+ * Tests various ThreadPoolExecutor implementations.
+ *
+ * @author Thomas Pantelis
+ */
+public class ThreadPoolExecutorTest {
+
+    private ExecutorService executor;
+
+    @After
+    public void tearDown() {
+        if( executor != null ) {
+            executor.shutdownNow();
+        }
+    }
+
+    @Test
+    public void testFastThreadPoolExecution() throws Exception {
+
+        testThreadPoolExecution(
+                SpecialExecutors.newBoundedFastThreadPool( 50, 100000, "TestPool" ),
+                100000, "TestPool", 0 );
+    }
+
+    @Test(expected=RejectedExecutionException.class)
+    public void testFastThreadPoolRejectingTask() throws Exception {
+
+        executor = SpecialExecutors.newBoundedFastThreadPool( 1, 1, "TestPool" );
+
+        for( int i = 0; i < 5; i++ ) {
+            executor.execute( new Task( null, null, null, null,
+                    TimeUnit.MICROSECONDS.convert( 5, TimeUnit.SECONDS ) ) );
+        }
+    }
+
+    @Test
+    public void testBlockingFastThreadPoolExecution() throws Exception {
+
+        // With a queue capacity of 1, it should block at some point.
+        testThreadPoolExecution(
+                SpecialExecutors.newBlockingBoundedFastThreadPool( 2, 1, "TestPool" ),
+                1000, null, 10 );
+    }
+
+    @Test
+    public void testCachedThreadPoolExecution() throws Exception {
+
+        testThreadPoolExecution(
+                SpecialExecutors.newBoundedCachedThreadPool( 10, 100000, "TestPool" ),
+                100000, "TestPool", 0 );
+    }
+
+    @Test(expected=RejectedExecutionException.class)
+    public void testCachedThreadRejectingTask() throws Exception {
+
+        ExecutorService executor = SpecialExecutors.newBoundedCachedThreadPool( 1, 1, "TestPool" );
+
+        for( int i = 0; i < 5; i++ ) {
+            executor.execute( new Task( null, null, null, null,
+                    TimeUnit.MICROSECONDS.convert( 5, TimeUnit.SECONDS ) ) );
+        }
+    }
+
+    @Test
+    public void testBlockingCachedThreadPoolExecution() throws Exception {
+
+        testThreadPoolExecution(
+                SpecialExecutors.newBlockingBoundedCachedThreadPool( 2, 1, "TestPool" ),
+                1000, null, 10 );
+    }
+
+    void testThreadPoolExecution( final ExecutorService executor,
+            final int numTasksToRun, final String expThreadPrefix, final long taskDelay ) throws Exception {
+
+        this.executor = executor;
+
+        System.out.println( "\nTesting " + executor.getClass().getSimpleName() + " with " +
+                numTasksToRun + " tasks." );
+
+        final CountDownLatch tasksRunLatch = new CountDownLatch( numTasksToRun );
+        final ConcurrentMap<Thread, AtomicLong> taskCountPerThread = new ConcurrentHashMap<>();
+        final AtomicReference<AssertionError> threadError = new AtomicReference<>();
+
+        Stopwatch stopWatch = new Stopwatch();
+        stopWatch.start();
+
+        new Thread() {
+            @Override
+            public void run() {
+                for( int i = 0; i < numTasksToRun; i++ ) {
+//                    if(i%100 == 0) {
+//                        Uninterruptibles.sleepUninterruptibly( 20, TimeUnit.MICROSECONDS );
+//                    }
+
+                    executor.execute( new Task( tasksRunLatch, taskCountPerThread,
+                                                threadError, expThreadPrefix, taskDelay ) );
+                }
+            }
+        }.start();
+
+        boolean done = tasksRunLatch.await( 15, TimeUnit.SECONDS );
+
+        stopWatch.stop();
+
+        if( !done ) {
+            fail( (numTasksToRun - tasksRunLatch.getCount()) + " tasks out of " +
+                   numTasksToRun + " executed" );
+        }
+
+        if( threadError.get() != null ) {
+            throw threadError.get();
+        }
+
+        System.out.println( taskCountPerThread.size() + " threads used:" );
+        for( Map.Entry<Thread, AtomicLong> e : taskCountPerThread.entrySet() ) {
+            System.out.println( "  " + e.getKey().getName() + " - " + e.getValue() + " tasks" );
+        }
+
+        System.out.println( "\n" + executor );
+        System.out.println( "\nElapsed time: " + stopWatch );
+        System.out.println();
+    }
+
+    private static class Task implements Runnable {
+        final CountDownLatch tasksRunLatch;
+        final ConcurrentMap<Thread, AtomicLong> taskCountPerThread;
+        final AtomicReference<AssertionError> threadError;
+        final String expThreadPrefix;
+        final long delay;
+
+        Task( CountDownLatch tasksRunLatch, ConcurrentMap<Thread, AtomicLong> taskCountPerThread,
+                AtomicReference<AssertionError> threadError, String expThreadPrefix, long delay ) {
+            this.tasksRunLatch = tasksRunLatch;
+            this.taskCountPerThread = taskCountPerThread;
+            this.threadError = threadError;
+            this.expThreadPrefix = expThreadPrefix;
+            this.delay = delay;
+        }
+
+        @Override
+        public void run() {
+            try {
+                if( delay > 0 ) {
+                    try {
+                        TimeUnit.MICROSECONDS.sleep( delay );
+                    } catch( InterruptedException e ) {}
+                }
+
+                if( expThreadPrefix != null ) {
+                    assertEquals( "Thread name starts with " + expThreadPrefix, true,
+                            Thread.currentThread().getName().startsWith( expThreadPrefix ) );
+                }
+
+                if( taskCountPerThread != null ) {
+                    AtomicLong count = taskCountPerThread.get( Thread.currentThread() );
+                    if( count == null ) {
+                        count = new AtomicLong( 0 );
+                        AtomicLong prev = taskCountPerThread.putIfAbsent( Thread.currentThread(), count );
+                        if( prev != null ) {
+                            count = prev;
+                        }
+                    }
+
+                    count.incrementAndGet();
+                }
+
+            } catch( AssertionError e ) {
+                if( threadError != null ) {
+                    threadError.set( e );
+                }
+            } finally {
+                if( tasksRunLatch != null ) {
+                    tasksRunLatch.countDown();
+                }
+            }
+        }
+    }
+}
index a8eef46d22944e10093fa2e4e60f970e79fd4714..a7e7cc77ada2232130cc69f2ce53e7cf9858615c 100644 (file)
@@ -29,7 +29,7 @@
         <module>yang-model-util</module>
         <module>yang-parser-api</module>
         <module>yang-parser-impl</module>
-        <module>yang-data-json</module>
+        <module>yang-data-composite-node</module>
     </modules>
     <build>
         <plugins>
index fab50614f2dfccf85e73649b88d2c7071faec5ee..e9ddbfb2f151ffeb3ab8702cc6c03dbfeca38060 100644 (file)
@@ -9,6 +9,10 @@ package org.opendaylight.yangtools.yang.binding.util;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
+
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.ParameterizedType;
@@ -21,10 +25,10 @@ import java.util.concurrent.locks.Lock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Joiner;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Supplier;
-
+/**
+ * @deprecated Use {@link org.opendaylight.yangtools.util.ClassLoaderUtils} instead.
+ */
+@Deprecated
 public final class ClassLoaderUtils {
     private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderUtils.class);
 
@@ -58,17 +62,17 @@ public final class ClassLoaderUtils {
     }
 
     /**
-    *
-    * Runs {@link Callable} with provided {@link ClassLoader}.
-    *
-    * Invokes supplies function and makes sure that original {@link ClassLoader}
-    * is context {@link ClassLoader} after execution.
-    *
-    * @param cls {@link ClassLoader} to be used.
-    * @param function Function to be executed.
-    * @return Result of callable invocation.
-    *
-    */
+     *
+     * Runs {@link Callable} with provided {@link ClassLoader}.
+     *
+     * Invokes supplies function and makes sure that original {@link ClassLoader}
+     * is context {@link ClassLoader} after execution.
+     *
+     * @param cls {@link ClassLoader} to be used.
+     * @param function Function to be executed.
+     * @return Result of callable invocation.
+     *
+     */
     public static <V> V withClassLoader(final ClassLoader cls, final Callable<V> function) throws Exception {
         checkNotNull(cls, "Classloader should not be null");
         checkNotNull(function, "Function should not be null");
@@ -82,20 +86,20 @@ public final class ClassLoaderUtils {
         }
     }
 
-   /**
-    *
-    * Runs {@link Callable} with provided {@link ClassLoader} and Lock.
-    *
-    * Invokes supplies function after acquiring lock
-    * and makes sure that original {@link ClassLoader}
-    * is context {@link ClassLoader} and lock is unlocked
-    * after execution.
-    *
-    * @param cls {@link ClassLoader} to be used.
-    * @param function Function to be executed.
-    * @return Result of Callable invocation.
-    *
-    */
+    /**
+     *
+     * Runs {@link Callable} with provided {@link ClassLoader} and Lock.
+     *
+     * Invokes supplies function after acquiring lock
+     * and makes sure that original {@link ClassLoader}
+     * is context {@link ClassLoader} and lock is unlocked
+     * after execution.
+     *
+     * @param cls {@link ClassLoader} to be used.
+     * @param function Function to be executed.
+     * @return Result of Callable invocation.
+     *
+     */
     public static <V> V withClassLoaderAndLock(final ClassLoader cls, final Lock lock, final Supplier<V> function) {
         checkNotNull(lock, "Lock should not be null");
 
@@ -138,9 +142,9 @@ public final class ClassLoaderUtils {
             String potentialOuter;
             int length = components.length;
             if (length > 2 && (potentialOuter = components[length - 2]) != null && Character.isUpperCase(potentialOuter.charAt(0))) {
-                    String outerName = Joiner.on(".").join(Arrays.asList(components).subList(0, length - 1));
-                    String innerName = outerName + "$" + components[length-1];
-                    return cls.loadClass(innerName);
+                String outerName = Joiner.on(".").join(Arrays.asList(components).subList(0, length - 1));
+                String innerName = outerName + "$" + components[length-1];
+                return cls.loadClass(innerName);
             } else {
                 throw e;
             }
similarity index 76%
rename from yang/yang-data-json/pom.xml
rename to yang/yang-data-composite-node/pom.xml
index 228b9d82d2892afcfd18ea547795548dcfc7d751..d4b1533cd2f055ac98759266f30e834061e6a401 100644 (file)
        </parent>
 
        <modelVersion>4.0.0</modelVersion>
-       <artifactId>yang-data-json</artifactId>
+       <artifactId>yang-data-composite-node</artifactId>
        <name>${project.artifactId}</name>
        <description>${project.artifactId}</description>
+    <packaging>bundle</packaging>
 
     <dependencies>
         <dependency>
         </dependency>
     </dependencies>
 
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Name>Composite node Normalized node transformator</Bundle-Name>
+            <Import-Package>*</Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
 </project>
diff --git a/yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/AnyXmlNodeCnSnParser.java b/yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/AnyXmlNodeCnSnParser.java
new file mode 100644 (file)
index 0000000..a13dd4b
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2013 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.yangtools.yang.data.composite.node.schema.cnsn.parser;
+
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.AnyXmlNodeBaseParser;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+
+public class AnyXmlNodeCnSnParser extends AnyXmlNodeBaseParser<Node<?>> {
+
+    public AnyXmlNodeCnSnParser() {
+        super();
+    }
+
+    @Override
+    protected Node<?> parseAnyXml(Node<?> element, AnyXmlSchemaNode schema) {
+        return element;
+    }
+}
@@ -5,13 +5,14 @@
  * 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.yangtools.yang.data.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
+
+import org.opendaylight.yangtools.yang.data.composite.node.schema.json.CnSnToNormalizedNodesUtils;
 
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.AugmentationNodeBaseParser;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.NodeParserDispatcher;
-import org.opendaylight.yangtools.yang.data.json.schema.json.CnSnToNormalizedNodesUtils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.LinkedListMultimap;
@@ -5,13 +5,14 @@
  * 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.yangtools.yang.data.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
+
+import org.opendaylight.yangtools.yang.data.composite.node.schema.json.CnSnToNormalizedNodesUtils;
 
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.ChoiceNodeBaseParser;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.NodeParserDispatcher;
-import org.opendaylight.yangtools.yang.data.json.schema.json.CnSnToNormalizedNodesUtils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.LinkedListMultimap;
@@ -5,9 +5,10 @@
  * 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.yangtools.yang.data.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
@@ -18,6 +19,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParser;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParserFactory;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.NodeParserDispatcher;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
@@ -35,11 +37,13 @@ public class CnSnToNormalizedNodeParserFactory implements ToNormalizedNodeParser
     private final MapEntryNodeCnSnParser mapEntryNodeCnSnParser;
     private final ChoiceNodeCnSnParser choiceNodeCnSnParser;
     private final AugmentationNodeCnSnParser augmentationNodeCnSnParser;
+    private final AnyXmlNodeCnSnParser anyXmlNodeCnSnParser;
 
     private CnSnToNormalizedNodeParserFactory() {
         leafNodeCnSnParser = new LeafNodeCnSnParser();
         leafSetEntryNodeCnSnParser = new LeafSetEntryNodeCnSnParser();
         leafSetNodeCnSnParser = new LeafSetNodeCnSnParser(leafSetEntryNodeCnSnParser);
+        anyXmlNodeCnSnParser = new AnyXmlNodeCnSnParser();
 
         final NodeParserDispatcher<Node<?>> dispatcher = new NodeParserDispatcher.BaseNodeParserDispatcher<Node<?>>(
                 this) {
@@ -51,6 +55,7 @@ public class CnSnToNormalizedNodeParserFactory implements ToNormalizedNodeParser
         mapNodeCnSnParser = new MapNodeCnSnParser(mapEntryNodeCnSnParser);
         choiceNodeCnSnParser = new ChoiceNodeCnSnParser(dispatcher);
         augmentationNodeCnSnParser = new AugmentationNodeCnSnParser(dispatcher);
+
     }
 
     public static CnSnToNormalizedNodeParserFactory getInstance() {
@@ -96,4 +101,9 @@ public class CnSnToNormalizedNodeParserFactory implements ToNormalizedNodeParser
     public ToNormalizedNodeParser<Node<?>, MapEntryNode, ListSchemaNode> getMapEntryNodeParser() {
         return mapEntryNodeCnSnParser;
     }
+
+    @Override
+    public ToNormalizedNodeParser<Node<?>, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeParser() {
+        return anyXmlNodeCnSnParser;
+    }
 }
@@ -5,7 +5,9 @@
  * 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.yangtools.yang.data.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
+
+import org.opendaylight.yangtools.yang.data.composite.node.schema.json.CnSnToNormalizedNodesUtils;
 
 import java.util.Collections;
 import java.util.Map;
@@ -14,7 +16,6 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.ContainerNodeBaseParser;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.NodeParserDispatcher;
-import org.opendaylight.yangtools.yang.data.json.schema.json.CnSnToNormalizedNodesUtils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.LinkedListMultimap;
@@ -5,7 +5,7 @@
  * 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.yangtools.yang.data.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
 
 import java.util.Collections;
 import java.util.Map;
@@ -5,7 +5,7 @@
  * 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.yangtools.yang.data.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
 
 import java.util.Collections;
 import java.util.Map;
@@ -5,7 +5,7 @@
  * 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.yangtools.yang.data.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
@@ -5,7 +5,9 @@
  * 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.yangtools.yang.data.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
+
+import org.opendaylight.yangtools.yang.data.composite.node.schema.json.CnSnToNormalizedNodesUtils;
 
 import java.util.Collections;
 import java.util.Map;
@@ -14,7 +16,6 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.MapEntryNodeBaseParser;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.NodeParserDispatcher;
-import org.opendaylight.yangtools.yang.data.json.schema.json.CnSnToNormalizedNodesUtils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.LinkedListMultimap;
@@ -5,7 +5,7 @@
  * 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.yangtools.yang.data.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
diff --git a/yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/AnyXmlNodeCnSnSerializer.java b/yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/AnyXmlNodeCnSnSerializer.java
new file mode 100644 (file)
index 0000000..db941ed
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 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.yangtools.yang.data.composite.node.schema.cnsn.serializer;
+
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.AnyXmlNodeBaseSerializer;
+
+public class AnyXmlNodeCnSnSerializer extends AnyXmlNodeBaseSerializer<Node<?>> {
+
+    @Override
+    protected Node<?> serializeAnyXml(AnyXmlNode node) {
+        return node.getValue();
+    }
+}
@@ -5,7 +5,7 @@
  * 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.yangtools.yang.data.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.AugmentationNodeBaseSerializer;
@@ -5,7 +5,7 @@
  * 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.yangtools.yang.data.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.ChoiceNodeBaseSerializer;
@@ -5,9 +5,10 @@
  * 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.yangtools.yang.data.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -19,6 +20,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializer;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializerFactory;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
@@ -34,6 +36,7 @@ public final class CnSnFromNormalizedNodeSerializerFactory implements FromNormal
     private final MapNodeCnSnSerializer mapNodeSerializer;
     private final LeafSetEntryNodeCnSnSerializer leafSetEntryNodeSerializer;
     private final MapEntryNodeCnSnSerializer mapEntryNodeSerializer;
+    private final AnyXmlNodeCnSnSerializer anyXmlNodeSerializer;
 
     private CnSnFromNormalizedNodeSerializerFactory() {
         final NodeSerializerDispatcher.BaseNodeSerializerDispatcher<Node<?>> dispatcher = new NodeSerializerDispatcher.BaseNodeSerializerDispatcher<Node<?>>(
@@ -45,6 +48,7 @@ public final class CnSnFromNormalizedNodeSerializerFactory implements FromNormal
         choiceSerializer = new ChoiceNodeCnSnSerializer(dispatcher);
         augmentSerializer = new AugmentationNodeCnSnSerializer(dispatcher);
         leafNodeSerializer = new LeafNodeCnSnSerializer();
+        anyXmlNodeSerializer = new AnyXmlNodeCnSnSerializer();
 
         leafSetEntryNodeSerializer = new LeafSetEntryNodeCnSnSerializer();
         leafSetSerializer = new LeafSetNodeCnSnSerializer(leafSetEntryNodeSerializer);
@@ -99,4 +103,9 @@ public final class CnSnFromNormalizedNodeSerializerFactory implements FromNormal
     public FromNormalizedNodeSerializer<Node<?>, MapEntryNode, ListSchemaNode> getMapEntryNodeSerializer() {
         return mapEntryNodeSerializer;
     }
+
+    @Override
+    public FromNormalizedNodeSerializer<Node<?>, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeSerializer() {
+        return anyXmlNodeSerializer;
+    }
 }
@@ -5,20 +5,17 @@
  * 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.yangtools.yang.data.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import com.google.common.base.Preconditions;
-
 import java.util.Collections;
 import java.util.List;
-
-import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.api.MutableNode;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.ContainerNodeBaseSerializer;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 
 public class ContainerNodeCnSnSerializer extends ContainerNodeBaseSerializer<Node<?>> {
@@ -30,19 +27,11 @@ public class ContainerNodeCnSnSerializer extends ContainerNodeBaseSerializer<Nod
     }
 
     @Override
-    public List<Node<?>> serialize(final ContainerSchemaNode schema, final ContainerNode containerNode) {
-
-        MutableCompositeNode mutCompNode = NodeFactory.createMutableCompositeNode(containerNode.getNodeType(), null,
-                null, null, null);
-
-        for (Node<?> element : super.serialize(schema, containerNode)) {
-            if (element instanceof MutableNode<?>) {
-                ((MutableNode<?>) element).setParent(mutCompNode);
-            }
-            mutCompNode.getValue().add(element);
-        }
-
-        return Collections.<Node<?>>singletonList(mutCompNode);
+    public List<Node<?>> serialize(final ContainerSchemaNode schema, final ContainerNode node) {
+        CompositeNodeBuilder<ImmutableCompositeNode> compNodeBuilder = ImmutableCompositeNode.builder();
+        compNodeBuilder.setQName(node.getNodeType());
+        compNodeBuilder.addAll(super.serialize(schema, node));
+        return Collections.<Node<?>> singletonList(compNodeBuilder.toInstance());        
     }
 
     @Override
@@ -5,7 +5,7 @@
  * 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.yangtools.yang.data.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
@@ -5,7 +5,7 @@
  * 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.yangtools.yang.data.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
@@ -5,7 +5,7 @@
  * 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.yangtools.yang.data.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
@@ -5,20 +5,17 @@
  * 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.yangtools.yang.data.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import com.google.common.base.Preconditions;
-
 import java.util.Collections;
 import java.util.List;
-
-import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.api.MutableNode;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
-import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.MapEntryNodeBaseSerializer;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 
 public class MapEntryNodeCnSnSerializer extends MapEntryNodeBaseSerializer<Node<?>> {
@@ -30,19 +27,11 @@ public class MapEntryNodeCnSnSerializer extends MapEntryNodeBaseSerializer<Node<
     }
 
     @Override
-    public List<Node<?>> serialize(final ListSchemaNode schema, final MapEntryNode node) {
-
-        MutableCompositeNode mutCompNode = NodeFactory.createMutableCompositeNode(node.getNodeType(), null, null, null,
-                null);
-
-        for (Node<?> element : super.serialize(schema, node)) {
-            if (element instanceof MutableNode<?>) {
-                ((MutableNode<?>) element).setParent(mutCompNode);
-            }
-            mutCompNode.getValue().add(element);
-        }
-
-        return Collections.<Node<?>>singletonList(mutCompNode);
+    public List<Node<?>> serialize(ListSchemaNode schema, MapEntryNode node) {
+        CompositeNodeBuilder<ImmutableCompositeNode> compNodeBuilder = ImmutableCompositeNode.builder();
+        compNodeBuilder.setQName(node.getNodeType());
+        compNodeBuilder.addAll(super.serialize(schema, node));
+        return Collections.<Node<?>> singletonList(compNodeBuilder.toInstance());        
     }
 
     @Override
@@ -5,7 +5,7 @@
  * 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.yangtools.yang.data.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
@@ -5,7 +5,7 @@
  * 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.yangtools.yang.data.json.schema.json;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.json;
 
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
similarity index 69%
rename from yang/yang-data-json/src/test/java/org/opendaylight/yangtools/yang/data/json/schema/TestUtils.java
rename to yang/yang-data-composite-node/src/test/java/org/opendaylight/yangtools/yang/data/composite/node/schema/TestUtils.java
index f6eb9a3cc540a23c0a2ebeb34d2edebf0be9b3d4..d2d2a3de3ac3bf5a926dd7edd27aeac45d97da97 100644 (file)
@@ -5,10 +5,9 @@
  * 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.yangtools.yang.data.json.schema;
+package org.opendaylight.yangtools.yang.data.composite.node.schema;
 
 import static org.junit.Assert.assertNotNull;
-import static org.opendaylight.yangtools.yang.data.impl.NodeFactory.createMutableCompositeNode;
 import static org.opendaylight.yangtools.yang.data.impl.NodeFactory.createMutableSimpleNode;
 
 import java.io.File;
@@ -25,24 +24,24 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.parser.api.YangModelParser;
@@ -125,69 +124,61 @@ public class TestUtils {
      * /cnsn-to-normalized-node/simple-conainer.json
      */
     public static CompositeNode prepareCompositeNodeStruct() {
-        MutableCompositeNode cont = createMutableCompositeNode(QName.create(MODULE_BASE, "cont"), null, null, null,
-                null);
+        CompositeNodeBuilder<ImmutableCompositeNode> contBuilder = ImmutableCompositeNode.builder();
+        contBuilder.setQName(QName.create(MODULE_BASE, "cont"));
 
         // cont1
-        List<Node<?>> contChilds = new ArrayList<>();
-        contChilds.add(createMutableCompositeNode(QName.create(MODULE_BASE, "cont1"),
-                cont,
-                Collections.<Node<?>> emptyList(), null, null));
+        contBuilder.add(ImmutableCompositeNode.builder().setQName(QName.create(MODULE_BASE, "cont1")).toInstance());
 
         // cont2
-        MutableCompositeNode cont2 = createMutableCompositeNode(QName.create(MODULE_BASE, "cont2"), cont, null, null,
-                null);
-        List<Node<?>> cont2Childs = new ArrayList<>();
-        cont2Childs.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf21"), cont2,
-                "value in cont2/lf21", null, null));
-        cont2.setValue(cont2Childs);
-        contChilds.add(cont2);
+        CompositeNodeBuilder<ImmutableCompositeNode> cont2 = ImmutableCompositeNode.builder().setQName(
+                QName.create(MODULE_BASE, "cont2"));
+        cont2.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf21"), null, "value in cont2/lf21", null, null));
+        contBuilder.add(cont2.toInstance());
 
         // lst1
-        contChilds.add(createMutableCompositeNode(QName.create(MODULE_BASE, "lst1"), cont,
-                Collections.<Node<?>> emptyList(), null, null));
+        contBuilder.add(ImmutableCompositeNode.builder().setQName(QName.create(MODULE_BASE, "lst1")).toInstance());
 
         // lst2
-        MutableCompositeNode lst2_1 = createMutableCompositeNode(QName.create(MODULE_BASE, "lst2"), cont, null, null,
-                null);
-        List<Node<?>> lst2_1Childs = new ArrayList<>();
-        lst2_1Childs
-        .add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf21"),
-                lst2_1,
-                "some value21", null, null));
-        lst2_1.setValue(lst2_1Childs);
-        contChilds.add(lst2_1);
-
-        MutableCompositeNode lst2_2 = createMutableCompositeNode(QName.create(MODULE_BASE, "lst2"), cont, null, null,
-                null);
-        List<Node<?>> lst2_2Childs = new ArrayList<>();
-        lst2_2Childs
-        .add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf22"), lst2_2, "some value22", null, null));
-        lst2_2.setValue(lst2_2Childs);
-        contChilds.add(lst2_2);
+        CompositeNodeBuilder<ImmutableCompositeNode> lst2_1Builder = ImmutableCompositeNode.builder().setQName(
+                QName.create(MODULE_BASE, "lst2"));
+        lst2_1Builder.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf21"), null, "some value21", null, null));
+        contBuilder.add(lst2_1Builder.toInstance());
+
+        CompositeNodeBuilder<ImmutableCompositeNode> lst2_2Builder = ImmutableCompositeNode.builder().setQName(
+                QName.create(MODULE_BASE, "lst2"));
+        lst2_2Builder.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf22"), null, "some value22", null, null));
+        contBuilder.add(lst2_2Builder.toInstance());
 
         // lflst1
-        contChilds.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lflst1"), cont, "lflst1_1", null, null));
-        contChilds.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lflst1"), cont, "lflst1_2", null, null));
+        contBuilder.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lflst1"), null, "lflst1_1", null, null));
+        contBuilder.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lflst1"), null, "lflst1_2", null, null));
 
         // lf1
-        contChilds.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf1"), cont, "lf1", null, null));
+        contBuilder.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf1"), null, "lf1", null, null));
 
         // lf11
-        contChilds.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf11"), cont, "value from case (cs1)", null,
+        contBuilder.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf11"), null, "value from case (cs1)", null,
                 null));
 
         // cont3
-        MutableCompositeNode cont3 = createMutableCompositeNode(QName.create(MODULE_AUGMENT, "cont3"), cont, null,
-                null, null);
-        List<Node<?>> cont3Childs = new ArrayList<>();
-        cont3Childs.add(createMutableSimpleNode(QName.create(MODULE_AUGMENT, "lf31"), cont3,
+        CompositeNodeBuilder<ImmutableCompositeNode> cont3Builder = ImmutableCompositeNode.builder().setQName(
+                QName.create(MODULE_AUGMENT, "cont3"));
+        cont3Builder.add(createMutableSimpleNode(QName.create(MODULE_AUGMENT, "lf31"), null,
                 "value in leaf in augment", null, null));
-        cont3.setValue(cont3Childs);
-        contChilds.add(cont3);
+        contBuilder.add(cont3Builder.toInstance());
+
+        // anxml-composite
+        CompositeNodeBuilder<ImmutableCompositeNode> anxmlCompositeBuilder = ImmutableCompositeNode.builder().setQName(
+                QName.create(MODULE_BASE, "anxml-composite"));
+        anxmlCompositeBuilder.add(NodeFactory.createImmutableSimpleNode(QName.create(MODULE_BASE, "anxml-cont"), null,
+                null));
+        contBuilder.add(anxmlCompositeBuilder.toInstance());
 
-        cont.setValue(contChilds);
-        return cont;
+        // anxml-simple
+        contBuilder.add(NodeFactory.createImmutableSimpleNode(QName.create(MODULE_BASE, "anxml-simple"), null,43));
+
+        return contBuilder.toInstance();
     }
 
     /**
@@ -203,7 +194,7 @@ public class TestUtils {
                 .withNodeIdentifier(getNodeIdentifier("cont2"))
                 .withChild(
                         Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("lf21"))
-                        .withValue("value in cont2/lf21").build()).build());
+                                .withValue("value in cont2/lf21").build()).build());
 
         CollectionNodeBuilder<MapEntryNode, MapNode> lst1 = Builders.mapBuilder().withNodeIdentifier(
                 getNodeIdentifier("lst1"));
@@ -249,7 +240,7 @@ public class TestUtils {
                 .withNodeIdentifier(getNodeIdentifier("chc"))
                 .withChild(
                         Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("lf11"))
-                        .withValue("value from case (cs1)").build()).build());
+                                .withValue("value from case (cs1)").build()).build());
 
         Set<QName> children = new HashSet<>();
         children.add(QName.create(MODULE_AUGMENT, "cont3"));
@@ -259,11 +250,31 @@ public class TestUtils {
                 .withNodeIdentifier(getAugmentationIdentifier(null, null, null, children))
                 .withChild(
                         Builders.containerBuilder()
-                        .withNodeIdentifier(getNodeIdentifier(MODULE_AUGMENT, "cont3"))
-                        .withChild(
-                                Builders.leafBuilder()
-                                .withNodeIdentifier(getNodeIdentifier(MODULE_AUGMENT, "lf31"))
-                                .withValue("value in leaf in augment").build()).build()).build());
+                                .withNodeIdentifier(getNodeIdentifier(MODULE_AUGMENT, "cont3"))
+                                .withChild(
+                                        Builders.leafBuilder()
+                                                .withNodeIdentifier(getNodeIdentifier(MODULE_AUGMENT, "lf31"))
+                                                .withValue("value in leaf in augment").build()).build()).build());
+
+        containerBuilder.withChild(Builders
+                .anyXmlBuilder()
+                .withNodeIdentifier(getNodeIdentifier("anxml-composite"))
+                .withValue(
+                        ImmutableCompositeNode
+                                .builder()
+                                .setQName(QName.create("simple:container:yang", "2013-11-12", "anxml-composite"))
+                                .add(NodeFactory.createImmutableSimpleNode(
+                                        QName.create("simple:container:yang", "2013-11-12", "anxml-cont"), null, null))
+                                .toInstance()).build());
+
+        containerBuilder
+                .withChild(Builders
+                        .anyXmlBuilder()
+                        .withNodeIdentifier(getNodeIdentifier("anxml-simple"))
+                        .withValue(
+                                NodeFactory.createImmutableSimpleNode(
+                                        QName.create("simple:container:yang", "2013-11-12", "anxml-simple"), null, 43))
+                        .build());
 
         ContainerNode build = containerBuilder.build();
         return build;
@@ -5,10 +5,15 @@
  * 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.yangtools.yang.data.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.parser;
 
 import static org.junit.Assert.assertEquals;
 
+import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory;
+
+
+import org.opendaylight.yangtools.yang.data.composite.node.schema.TestUtils;
+
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
@@ -19,7 +24,6 @@ import org.junit.Test;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.json.schema.TestUtils;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
@@ -5,11 +5,16 @@
  * 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.yangtools.yang.data.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.serializer;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
+import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer.CnSnFromNormalizedNodeSerializerFactory;
+
+
+import org.opendaylight.yangtools.yang.data.composite.node.schema.TestUtils;
+
 import java.net.URISyntaxException;
 import java.util.Set;
 
@@ -19,7 +24,6 @@ import org.junit.Test;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.json.schema.TestUtils;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
similarity index 82%
rename from yang/yang-data-json/src/test/resources/cnsn-to-normalized-node/json/simple-container.json
rename to yang/yang-data-composite-node/src/test/resources/cnsn-to-normalized-node/json/simple-container.json
index b840efbd8f30e7881e5c1c9557952632eb8b9425..37dd7c0732177aa2001469715a2f046dcf996ad9 100644 (file)
         "lf1":"lf1",
         
         "lf11":"value from case (cs1)"
+        "anxml-composite": {            
+            "anxml-cont" {
+            }
+        }
+        
+        "anxml-simple":43;
     }
 }
\ No newline at end of file
index 48fedda8606869730fdcf8ee339b13c41c9eb4ed..319752d282bf61a3b8ae43ef6e42eb8701b7d37e 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.transform;
 
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -15,6 +16,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
@@ -36,4 +38,5 @@ public interface FromNormalizedNodeSerializerFactory<E> {
        FromNormalizedNodeSerializer<E, LeafSetNode<?>, LeafListSchemaNode> getLeafSetNodeSerializer();
        FromNormalizedNodeSerializer<E, MapEntryNode, ListSchemaNode> getMapEntryNodeSerializer();
        FromNormalizedNodeSerializer<E, MapNode, ListSchemaNode> getMapNodeSerializer();
+       FromNormalizedNodeSerializer<E, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeSerializer();
 }
index f99cbcff281d8b708d2a232d6a083fde21bb2b6a..7c2700f7c4139f83208845e570c194b1d445a5aa 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.transform;
 
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -15,6 +16,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
@@ -36,4 +38,5 @@ public interface ToNormalizedNodeParserFactory<E> {
        ToNormalizedNodeParser<E, LeafSetNode<?>, LeafListSchemaNode> getLeafSetNodeParser();
        ToNormalizedNodeParser<E, MapEntryNode, ListSchemaNode> getMapEntryNodeParser();
        ToNormalizedNodeParser<E, MapNode, ListSchemaNode> getMapNodeParser();
+        ToNormalizedNodeParser<E, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeParser();
 }
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/parser/AnyXmlNodeBaseParser.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/parser/AnyXmlNodeBaseParser.java
new file mode 100644 (file)
index 0000000..2730367
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013 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.yangtools.yang.data.impl.schema.transform.base.parser;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParser;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+
+/**
+ * Abstract(base) parser for LeafNodes, parses elements of type E.
+ *
+ * @param <E> type of elements to be parsed
+ */
+public abstract class AnyXmlNodeBaseParser<E> implements
+        ToNormalizedNodeParser<E, AnyXmlNode, AnyXmlSchemaNode> {
+
+    @Override
+    public final AnyXmlNode parse(Iterable<E> elements, AnyXmlSchemaNode schema) {
+        final int size = Iterables.size(elements);
+        Preconditions.checkArgument(size == 1, "Elements mapped to any-xml node illegal count: %s", size);
+
+        final E e = elements.iterator().next();
+        Node<?> value = parseAnyXml(e, schema);
+
+        NormalizedNodeAttrBuilder<NodeIdentifier, Node<?>, AnyXmlNode> anyXmlBuilder = Builders.anyXmlBuilder(schema);
+
+        return anyXmlBuilder.withValue(value).build();
+    }
+
+    /**
+     *
+     * Parse the inner value of a AnyXmlNode from element of type E.
+     *
+     * @param element to be parsed
+     * @param schema schema for leaf
+     * @return parsed element as an Object
+     */
+    protected abstract Node<?> parseAnyXml(E element, AnyXmlSchemaNode schema);
+
+}
index 40722b3aee8a15c05f37c9974e475f7fc67d5478..c316e72b02a59e3fe2a1aaf4441efca4c43cf80a 100644 (file)
@@ -7,10 +7,11 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser;
 
+import com.google.common.base.Preconditions;
 import java.util.List;
-
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
@@ -18,8 +19,6 @@ import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 
-import com.google.common.base.Preconditions;
-
 /**
  *
  * Dispatches the parsing process of elements according to schema and returns the parsed Node.
@@ -57,6 +56,8 @@ public interface NodeParserDispatcher<E> {
                 return factory.getChoiceNodeParser().parse(childNodes, (ChoiceNode) schema);
             } else if (schema instanceof AugmentationSchema) {
                 return factory.getAugmentationNodeParser().parse(childNodes, (AugmentationSchema) schema);
+            } else if (schema instanceof AnyXmlSchemaNode) {
+                return factory.getAnyXmlNodeParser().parse(childNodes,(AnyXmlSchemaNode)schema);
             }
 
             throw new IllegalArgumentException("Unable to parse node, unknown schema type: " + schema.getClass());
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/serializer/AnyXmlNodeBaseSerializer.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/serializer/AnyXmlNodeBaseSerializer.java
new file mode 100644 (file)
index 0000000..e2fd60b
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013 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.yangtools.yang.data.impl.schema.transform.base.serializer;
+
+import java.util.Collections;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+
+/**
+ * Abstract(base) serializer for AnyXmlNodes, serializes elements of type E.
+ *
+ * @param <E> type of serialized elements
+ */
+public abstract class AnyXmlNodeBaseSerializer<E> implements
+        FromNormalizedNodeSerializer<E, AnyXmlNode, AnyXmlSchemaNode> {
+
+    @Override
+    public final Iterable<E> serialize(AnyXmlSchemaNode schema, AnyXmlNode node) {
+        return Collections.singletonList(serializeAnyXml(node));
+    }
+
+    /**
+     *
+     * Serialize the inner value of a AnyXmlNode into element of type E.
+     *
+     * @param node to be serialized
+     * @param schema schema for leaf
+     * @return serialized inner value as an Element
+     */
+    protected abstract E serializeAnyXml(AnyXmlNode node);
+}
index d08a9fbf8d713ad31fd4a83aea1e69639992943a..c686c31cba4871577c415937262c62b00c5046fd 100644 (file)
@@ -7,7 +7,10 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer;
 
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -17,20 +20,20 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MixinNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializerFactory;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Iterables;
-
 /**
  *
- * Dispatches the serialization process of nodes according to schema and returns the serialized elements.
+ * Dispatches the serialization process of nodes according to schema and returns
+ * the serialized elements.
  *
- * @param <E> type of serialized elements
+ * @param <E>
+ *            type of serialized elements
  */
 public interface NodeSerializerDispatcher<E> {
 
@@ -38,8 +41,9 @@ public interface NodeSerializerDispatcher<E> {
             DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> dataContainerChild);
 
     /**
-     * Abstract implementation that implements the dispatch conditions. Only requires serializers to be provided.
-     * The same instance of serializer can be provided in case it is immutable.
+     * Abstract implementation that implements the dispatch conditions. Only
+     * requires serializers to be provided. The same instance of serializer can
+     * be provided in case it is immutable.
      */
     public static abstract class BaseNodeSerializerDispatcher<E> implements NodeSerializerDispatcher<E> {
         private final FromNormalizedNodeSerializerFactory<E> factory;
@@ -55,6 +59,8 @@ public interface NodeSerializerDispatcher<E> {
                 return onContainerNode(childSchema, dataContainerChild);
             } else if (dataContainerChild instanceof LeafNode<?>) {
                 return onLeafNode(childSchema, dataContainerChild);
+            } else if (dataContainerChild instanceof AnyXmlNode) {
+                return onAnyXmlNode(childSchema, dataContainerChild);
             } else if (dataContainerChild instanceof MixinNode) {
                 if (dataContainerChild instanceof LeafSetNode<?>) {
                     return onLeafListNode(childSchema, dataContainerChild);
@@ -107,6 +113,15 @@ public interface NodeSerializerDispatcher<E> {
             return elements;
         }
 
+        private Iterable<E> onAnyXmlNode(Object childSchema,
+                DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> dataContainerChild) {
+            checkSchemaCompatibility(childSchema, AnyXmlSchemaNode.class, dataContainerChild);
+            Iterable<E> elements = factory.getAnyXmlNodeSerializer().serialize((AnyXmlSchemaNode) childSchema,
+                    (AnyXmlNode) dataContainerChild);
+            checkOnlyOneSerializedElement(elements, dataContainerChild);
+            return elements;
+        }
+
         private static void checkOnlyOneSerializedElement(Iterable<?> elements,
                 DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> dataContainerChild) {
             final int size = Iterables.size(elements);
index 53d68f887267719865f9be0b50bb8e907c479533..a02b60b2fd24b6443596e3dd4fe4e508433b0518 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser;
 
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -19,6 +20,7 @@ import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParser;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParserFactory;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.NodeParserDispatcher;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
@@ -95,4 +97,9 @@ public final class DomToNormalizedNodeParserFactory implements ToNormalizedNodeP
     public ToNormalizedNodeParser<Element, MapNode, ListSchemaNode> getMapNodeParser() {
         return mapNodeParser;
     }
+
+    @Override
+    public ToNormalizedNodeParser<Element, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeParser() {
+        throw new UnsupportedOperationException();
+    }
 }
index 0a5aba7c4a67789935bd98770d85e07e2cb1d8c6..b9d8ef2a757a3dec9cd0f3faf00d2194fe02a24a 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer;
 
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -19,6 +20,7 @@ import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializer;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializerFactory;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
@@ -98,4 +100,9 @@ public final class DomFromNormalizedNodeSerializerFactory implements FromNormali
                return mapNodeSerializer;
        }
 
+        @Override
+        public FromNormalizedNodeSerializer<Element, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeSerializer() {
+            throw new UnsupportedOperationException();
+        }
+
 }
index 28bb2d9b70fae8919781070080404200e6a9e308..dc2cbd1e676460ec7138752c2b1d0716637b7089 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>additional-config</artifactId>
@@ -20,7 +20,7 @@
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-maven-plugin-spi</artifactId>
-            <version>${it-project.version}</version>
+            <version>${project.version}</version>
         </dependency>
     </dependencies>
 
@@ -29,7 +29,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -71,7 +71,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 82640e8ee674c05da066fd37e43e607cef2ab3c2..d99dd0319a341fe919aef2f8c360f1bf7b25824a 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>correct</artifactId>
@@ -20,7 +20,7 @@
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-maven-plugin-spi</artifactId>
-            <version>${it-project.version}</version>
+            <version>${project.version}</version>
         </dependency>
     </dependencies>
 
@@ -29,7 +29,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -56,7 +56,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index cd7be965d193bb026422989970260e4dc81d5068..3d931010a355f00051f96274b3755acf487e9289 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>generator-test1</artifactId>
@@ -25,7 +25,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -51,7 +51,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 948c025898f9c0aa7b3040fe1a43fa2aac5e1e59..3063698e67e7b56a1263bf9e0ca093c966029905 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>generator-test2</artifactId>
@@ -24,9 +24,9 @@
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>generator-test1</artifactId>
-            <version>${it-project.version}</version>
+            <version>${project.version}</version>
             <scope>system</scope>
-            <systemPath>${project.basedir}/../GenerateTest1/target/generator-test1-1.0.jar</systemPath>
+            <systemPath>${project.basedir}/../GenerateTest1/target/generator-test1-${project.version}.jar</systemPath>
         </dependency>
     </dependencies>
 
@@ -35,7 +35,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -61,7 +61,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index be217f8d6255961d34c8fa3ffcadb382a22d2461..c764eca085f5d39f41422a4206abce176417e671 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>generator</artifactId>
@@ -21,7 +21,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -48,7 +48,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 8c45963a8d63ded42e593aa1b9a3c5431ca4b6a8..2e9982c5dd8bd423de49401ce6c27632d40eb14c 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>invalid-version</artifactId>
@@ -48,7 +48,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -76,7 +76,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 8ade2b1a9cff9883cc9df15831b40ee6f0bc0991..5641d3ac275f86cdfb7d947735e6117c89a4b078 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>missing-yang-in-dep</artifactId>
@@ -21,7 +21,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -58,7 +58,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 6fc2c8070530474f06cb82cd7cebec991ff63588..0fae6f793252995fb3fb30d911965f0d209ec469 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>naming-conflict</artifactId>
@@ -21,7 +21,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -46,7 +46,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>maven-sal-api-gen-plugin</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>jar</type>
                     </dependency>
                 </dependencies>
index d236de3e0afa46f4a10baec305422b5935ed5620..0a91e17da1c767c29b094e38fc51646598ee3667 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>no-generators</artifactId>
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
                             <goal>generate-sources</goal>
                         </goals>
-                        <configuration>
+                        <configuration combine.self="override">
                             <yangFilesRootDir>../files</yangFilesRootDir>
                             <inspectDependencies>false</inspectDependencies>
                             <codeGenerators>
@@ -49,7 +49,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 20ba31ae929fe4a01dface59bbbca1836292c75c..0ead9af0c645f3291e2a252d0cc2f200866a1226 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>no-output-dir</artifactId>
@@ -21,7 +21,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -48,7 +48,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 3cac79593b097d8e8a4c3435e788fa7a0e9f7494..36603f33dbc71105c8f923fa4e42813f490edadf 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>no-yang-files</artifactId>
@@ -21,7 +21,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -48,7 +48,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 853e8e0c0e814af4ef001b87a3a83b0f2d72e711..b58576d5ea254f3e9fce2490250b10e1787fac7d 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>unknown-generator</artifactId>
@@ -21,7 +21,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -56,7 +56,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 8bb27a854b4154caed8c9d270c8ea8afa5e92e3e..1da608feb4dc7d0724a7aaf0a4d946d933d5daec 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>yang-root-not-exist</artifactId>
@@ -21,7 +21,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
index 58085fce5f61564df11893f9529434f4c1d915fc..18aa62fd4f3899cb9ea1c4bb734758896d9f42be 100644 (file)
@@ -8,15 +8,17 @@
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
+    <parent>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>yangtools-parent</artifactId>
+        <version>0.6.2-SNAPSHOT</version>
+        <relativePath>/../../common/parent/pom.xml</relativePath>
+    </parent>
+
     <groupId>org.opendaylight.yangtools</groupId>
     <artifactId>test-parent</artifactId>
-    <version>1.0</version>
     <packaging>pom</packaging>
 
-    <properties>
-        <it-project.version>0.6.2-SNAPSHOT</it-project.version>
-    </properties>
-
     <modules>
         <module>additional-config</module>
         <module>correct</module>
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java
new file mode 100644 (file)
index 0000000..4dd30b9
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.util;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformationException;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformer;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaTransformerRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AbstractSchemaRepository implements SchemaRepository, SchemaSourceRegistry {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractSchemaRepository.class);
+    private static final Comparator<SchemaTransformerRegistration> TRANSFORMER_COST_COMPARATOR = new Comparator<SchemaTransformerRegistration>() {
+        @Override
+        public int compare(final SchemaTransformerRegistration o1, final SchemaTransformerRegistration o2) {
+            return o1.getInstance().getCost() - o2.getInstance().getCost();
+        }
+    };
+
+    /*
+     * Output-type -> transformer map. Our usage involves knowing the destination type,
+     * so we have to work backwards and find a transformer chain which will get us
+     * to that representation given our available sources.
+     */
+    private final Multimap<Class<? extends SchemaSourceRepresentation>, SchemaTransformerRegistration> transformers =
+            HashMultimap.create();
+
+    /*
+     * Source identifier -> representation -> provider map. We usually are looking for
+     * a specific representation a source.
+     */
+    private final Map<SourceIdentifier, Multimap<Class<?>, AbstractSchemaSourceRegistration>> sources = new HashMap<>();
+
+
+    private static final <T extends SchemaSourceRepresentation> ListenableFuture<Optional<T>> fetchSource(final SourceIdentifier id, final Iterator<AbstractSchemaSourceRegistration> it) {
+        if (!it.hasNext()) {
+            return Futures.immediateFuture(Optional.<T>absent());
+        }
+
+        return Futures.transform(((SchemaSourceProvider<T>)it.next().getProvider()).getSource(id), new AsyncFunction<Optional<T>, Optional<T>>() {
+            @Override
+            public ListenableFuture<Optional<T>> apply(final Optional<T> input) throws Exception {
+                if (input.isPresent()) {
+                    return Futures.immediateFuture(input);
+                } else {
+                    return fetchSource(id, it);
+                }
+            }
+        });
+    }
+
+    private <T extends SchemaSourceRepresentation> ListenableFuture<Optional<T>> transformSchemaSource(final SourceIdentifier id, final Class<T> representation) {
+        final Multimap<Class<?>, AbstractSchemaSourceRegistration> srcs = sources.get(id);
+        if (srcs.isEmpty()) {
+            return Futures.immediateFailedFuture(new SchemaSourceTransformationException(
+                    String.format("No providers producing a representation of %s registered", id)));
+        }
+
+        final Collection<SchemaTransformerRegistration> ts = transformers.get(representation);
+        if (ts.isEmpty()) {
+            return Futures.immediateFailedFuture(new SchemaSourceTransformationException(
+                    String.format("No transformers producing representation %s registered", representation)));
+        }
+
+        // Build up the candidate list
+        final List<SchemaTransformerRegistration> candidates = new ArrayList<>();
+        for (SchemaTransformerRegistration tr : ts) {
+            final SchemaSourceTransformer<?, ?> t = tr.getInstance();
+            final Class<?> i = t.getInputRepresentation();
+            if (srcs.containsKey(i)) {
+                candidates.add(tr);
+            } else {
+                LOG.debug("Provider for {} in {} not found, skipping transfomer {}", id, i, t);
+            }
+        }
+
+        if (candidates.isEmpty()) {
+            return Futures.immediateFailedFuture(new SchemaSourceTransformationException(
+                    String.format("No matching source/transformer pair for source %s representation %s found", id, representation)));
+        }
+
+        Collections.sort(candidates, TRANSFORMER_COST_COMPARATOR);
+        // return transform(candidates.iterator(), id);
+        return null;
+    }
+
+    /**
+     * Obtain a SchemaSource is selected representation
+     */
+    protected <T extends SchemaSourceRepresentation> ListenableFuture<Optional<T>> getSchemaSource(final SourceIdentifier id, final Class<T> representation) {
+        final Multimap<Class<?>, AbstractSchemaSourceRegistration> srcs = sources.get(id);
+        if (srcs == null) {
+            LOG.debug("No providers registered for source {}", id);
+            return Futures.immediateFuture(Optional.<T>absent());
+        }
+
+        final Collection<AbstractSchemaSourceRegistration> candidates = srcs.get(representation);
+        return Futures.transform(AbstractSchemaRepository.<T>fetchSource(id, candidates.iterator()), new AsyncFunction<Optional<T>, Optional<T>>() {
+            @Override
+            public ListenableFuture<Optional<T>> apply(final Optional<T> input) throws Exception {
+                if (input.isPresent()) {
+                    return Futures.immediateFuture(input);
+                }
+
+                return transformSchemaSource(id, representation);
+            }
+        });
+    }
+
+    @Override
+    public SchemaContextFactory createSchemaContextFactory(final SchemaSourceFilter filter) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    private void addSource(final SourceIdentifier id, final Class<?> rep, final AbstractSchemaSourceRegistration reg) {
+        Multimap<Class<?>, AbstractSchemaSourceRegistration> m = sources.get(id);
+        if (m == null) {
+            m = HashMultimap.create();
+            sources.put(id, m);
+        }
+
+        m.put(rep, reg);
+    }
+
+    private void removeSource(final SourceIdentifier id, final Class<?> rep, final SchemaSourceRegistration reg) {
+        final Multimap<Class<?>, AbstractSchemaSourceRegistration> m = sources.get(id);
+        if (m != null) {
+            m.remove(rep, reg);
+            if (m.isEmpty()) {
+                sources.remove(m);
+            }
+        }
+    }
+
+    @Override
+    public <T extends SchemaSourceRepresentation> SchemaSourceRegistration registerSchemaSource(
+            final SourceIdentifier identifier, final SchemaSourceProvider<? super T> provider, final Class<T> representation) {
+        final AbstractSchemaSourceRegistration ret = new AbstractSchemaSourceRegistration(identifier, provider) {
+            @Override
+            protected void removeRegistration() {
+                removeSource(identifier, representation, this);
+            }
+        };
+
+        addSource(identifier, representation, ret);
+        return ret;
+    }
+
+    @Override
+    public SchemaTransformerRegistration registerSchemaSourceTransformer(final SchemaSourceTransformer<?, ?> transformer) {
+        final SchemaTransformerRegistration ret = new AbstractSchemaTransformerRegistration(transformer) {
+            @Override
+            protected void removeRegistration() {
+                transformers.remove(transformer.getOutputRepresentation(), this);
+            }
+        };
+
+        transformers.put(transformer.getOutputRepresentation(), ret);
+        return ret;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceRegistration.java
new file mode 100644 (file)
index 0000000..9ed0afe
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.util;
+
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+
+public abstract class AbstractSchemaSourceRegistration extends AbstractObjectRegistration<SourceIdentifier> implements SchemaSourceRegistration {
+    private final SchemaSourceProvider<?> provider;
+
+    protected AbstractSchemaSourceRegistration(final SourceIdentifier identifier, final SchemaSourceProvider<?> provider) {
+        super(identifier);
+        this.provider = Preconditions.checkNotNull(provider);
+    }
+
+    protected SchemaSourceProvider<?> getProvider() {
+        return provider;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaTransformerRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaTransformerRegistration.java
new file mode 100644 (file)
index 0000000..d264ae0
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.util;
+
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformer;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaTransformerRegistration;
+
+public abstract class AbstractSchemaTransformerRegistration extends AbstractObjectRegistration<SchemaSourceTransformer<?, ?>> implements SchemaTransformerRegistration {
+    protected AbstractSchemaTransformerRegistration(
+            final SchemaSourceTransformer<?, ?> transformer) {
+        super(transformer);
+    }
+}
index f4fb777ccd3c845fce80c5ce8c63645f34155e3a..ac9358c72eb55494f9affb955ff918747f8126d8 100644 (file)
@@ -108,7 +108,7 @@ END_IDENTIFIER_LEFT_BRACE : '{' ->type(LEFT_BRACE), popMode;
  
 fragment SUB_STRING : ('"' (ESC | ~["])*'"') | ('\'' (ESC | ~['])*'\'') ;
 
-STRING: ((~( '\r' | '\n' | '\t' | ' ' | ';' | '{' | '"' | '\'' )+) | SUB_STRING ) ->popMode;// IDENTIFIER ;
+STRING: ((~( '\r' | '\n' | '\t' | ' ' | ';' | '{' | '"' | '\'')~( '\r' | '\n' | '\t' | ' ' | ';' | '{' )* ) | SUB_STRING ) ->popMode;// IDENTIFIER ;
 S : [ \n\r\t] -> skip;    
 
 mode BLOCK_COMMENT_MODE;
index 13f5dbba5ec14bc6d500d0033068b7dff8358473..c7054fbc9f41120bdf92f96f52942b99444a63f4 100644 (file)
@@ -29,9 +29,9 @@ unknown_statement : (YIN_ELEMENT_KEYWORD | YANG_VERSION_KEYWORD | WHEN_KEYWORD |
                     NAMESPACE_KEYWORD | MUST_KEYWORD | MODULE_KEYWORD | MIN_ELEMENTS_KEYWORD | MAX_ELEMENTS_KEYWORD | MANDATORY_KEYWORD | LIST_KEYWORD | 
                     LENGTH_KEYWORD | LEAF_LIST_KEYWORD | LEAF_KEYWORD | KEY_KEYWORD | INPUT_KEYWORD | INCLUDE_KEYWORD | IMPORT_KEYWORD | IF_FEATURE_KEYWORD | 
                     IDENTITY_KEYWORD | GROUPING_KEYWORD | FRACTION_DIGITS_KEYWORD | FEATURE_KEYWORD | DEVIATE_KEYWORD | DEVIATION_KEYWORD | EXTENSION_KEYWORD | 
-                    ERROR_MESSAGE_KEYWORD | ERROR_APP_TAG_KEYWORD | ENUM_KEYWORD | DESCRIPTION_KEYWORD | DEFAULT_KEYWORD | CONTAINER_KEYWORD | CONTACT_KEYWORD | 
+                    ERROR_MESSAGE_KEYWORD | ERROR_APP_TAG_KEYWORD | ENUM_KEYWORD | DESCRIPTION_KEYWORD | STATUS_KEYWORD | DEFAULT_KEYWORD | CONTAINER_KEYWORD | CONTACT_KEYWORD | 
                     CONFIG_KEYWORD | CHOICE_KEYWORD |  CASE_KEYWORD | BIT_KEYWORD | BELONGS_TO_KEYWORD | BASE_KEYWORD | AUGMENT_KEYWORD |  
-                    ANYXML_KEYWORD | IDENTIFIER) string? (SEMICOLON | (LEFT_BRACE unknown_statement* | identifier_stmt* RIGHT_BRACE)*);
+                    ANYXML_KEYWORD | IDENTIFIER) string? (SEMICOLON | (LEFT_BRACE (unknown_statement | identifier_stmt)* RIGHT_BRACE)*);
 
 stmtend : (SEMICOLON) | (LEFT_BRACE identifier_stmt? RIGHT_BRACE);
 deviate_replace_stmt : DEVIATE_KEYWORD string /* REPLACE_KEYWORD */ (SEMICOLON | (LEFT_BRACE (identifier_stmt |type_stmt | units_stmt | default_stmt | config_stmt | mandatory_stmt | min_elements_stmt | max_elements_stmt )* RIGHT_BRACE));
@@ -63,11 +63,11 @@ short_case_stmt : container_stmt | leaf_stmt | leaf_list_stmt | list_stmt | anyx
 choice_stmt : CHOICE_KEYWORD string (SEMICOLON | (LEFT_BRACE  (identifier_stmt |when_stmt | if_feature_stmt | default_stmt | config_stmt | mandatory_stmt | status_stmt | description_stmt | reference_stmt | short_case_stmt | case_stmt)* RIGHT_BRACE));
 unique_stmt : UNIQUE_KEYWORD string stmtend;
 key_stmt : KEY_KEYWORD string stmtend;
-list_stmt : LIST_KEYWORD string LEFT_BRACE  (identifier_stmt |when_stmt | if_feature_stmt | must_stmt | key_stmt | unique_stmt | config_stmt | min_elements_stmt | max_elements_stmt | ordered_by_stmt | status_stmt | description_stmt | reference_stmt | typedef_stmt | grouping_stmt | data_def_stmt )* RIGHT_BRACE;
+list_stmt : LIST_KEYWORD string LEFT_BRACE  (when_stmt | if_feature_stmt | must_stmt | key_stmt | unique_stmt | config_stmt | min_elements_stmt | max_elements_stmt | ordered_by_stmt | status_stmt | description_stmt | reference_stmt | typedef_stmt | grouping_stmt | data_def_stmt | identifier_stmt)* RIGHT_BRACE;
 leaf_list_stmt : LEAF_LIST_KEYWORD string LEFT_BRACE  (identifier_stmt |when_stmt | if_feature_stmt | type_stmt | units_stmt | must_stmt | config_stmt | min_elements_stmt | max_elements_stmt | ordered_by_stmt | status_stmt | description_stmt | reference_stmt )* RIGHT_BRACE;
 leaf_stmt : LEAF_KEYWORD string LEFT_BRACE  (identifier_stmt |when_stmt | if_feature_stmt | type_stmt | units_stmt | must_stmt | default_stmt | config_stmt | mandatory_stmt | status_stmt | description_stmt | reference_stmt )* RIGHT_BRACE;
-container_stmt : CONTAINER_KEYWORD string (SEMICOLON | (LEFT_BRACE  (identifier_stmt | when_stmt | if_feature_stmt | must_stmt | presence_stmt | config_stmt | status_stmt | description_stmt | reference_stmt | typedef_stmt | grouping_stmt | data_def_stmt )* RIGHT_BRACE));
-grouping_stmt : GROUPING_KEYWORD string (SEMICOLON | (LEFT_BRACE  (identifier_stmt |status_stmt | description_stmt | reference_stmt | typedef_stmt | grouping_stmt | data_def_stmt )* RIGHT_BRACE));
+container_stmt : CONTAINER_KEYWORD string (SEMICOLON | (LEFT_BRACE  (when_stmt | if_feature_stmt | must_stmt | presence_stmt | config_stmt | status_stmt | description_stmt | reference_stmt | typedef_stmt | grouping_stmt | data_def_stmt | identifier_stmt)* RIGHT_BRACE));
+grouping_stmt : GROUPING_KEYWORD string (SEMICOLON | (LEFT_BRACE (status_stmt | description_stmt | reference_stmt | typedef_stmt | grouping_stmt | data_def_stmt | identifier_stmt)* RIGHT_BRACE));
 value_stmt : VALUE_KEYWORD string stmtend;
 max_value_arg : /*UNBOUNDED_KEYWORD |*/ string;
 min_value_arg : /*UNBOUNDED_KEYWORD |*/ string;
index 2757c10e1e755162440c1102b5d0ee37108e75b3..542506045d031de4381010e849d29f9aef56b614 100644 (file)
@@ -1069,10 +1069,12 @@ public final class ParserListenerUtils {
 
             if (parent instanceof TypeDefinitionBuilder) {
                 TypeDefinitionBuilder typedef = (TypeDefinitionBuilder) parent;
-                typedef.setRanges(rangeStatements);
-                typedef.setLengths(lengthStatements);
-                typedef.setPatterns(patternStatements);
-                typedef.setFractionDigits(fractionDigits);
+                if (!(typedef instanceof UnionTypeBuilder)) {
+                    typedef.setRanges(rangeStatements);
+                    typedef.setLengths(lengthStatements);
+                    typedef.setPatterns(patternStatements);
+                    typedef.setFractionDigits(fractionDigits);
+                }
                 return unknownType.build();
             } else {
                 TypeDefinition<?> baseType = unknownType.build();
index 5805cd7e8ccf4e5ce575ea003c001b0ea9000147..133c7b18a75d3b0d736607939086ddd5770016eb 100644 (file)
@@ -8,10 +8,12 @@
 package org.opendaylight.yangtools.yang.parser.impl;
 
 import com.google.common.collect.Sets;
+
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.HashSet;
 import java.util.Set;
+
 import org.antlr.v4.runtime.tree.ParseTree;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Anyxml_stmtContext;
@@ -66,7 +68,7 @@ import org.slf4j.LoggerFactory;
  * This validator expects only one module or submodule per file and performs
  * only basic validation where context from all yang models is not present.
  */
-final class YangModelBasicValidationListener extends YangParserBaseListener {
+public final class YangModelBasicValidationListener extends YangParserBaseListener {
     private static final Logger LOGGER = LoggerFactory.getLogger(YangModelBasicValidationListener.class);
     private final Set<String> uniquePrefixes = new HashSet<>();
     private final Set<String> uniqueImports = new HashSet<>();
index 0002c11187b5a723c482e85ca5efdc194c8430ef..1d0d48a7ddf2d7b5c034440d15f477c76b29e8fd 100644 (file)
@@ -22,6 +22,7 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
 import com.google.common.collect.HashBiMap;
 import com.google.common.io.ByteSource;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -37,7 +38,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
+
 import javax.annotation.concurrent.Immutable;
+
 import org.antlr.v4.runtime.ANTLRInputStream;
 import org.antlr.v4.runtime.CommonTokenStream;
 import org.antlr.v4.runtime.tree.ParseTree;
@@ -107,8 +110,7 @@ public final class YangParserImpl implements YangContextParser {
     }
 
     @Override
-    public SchemaContext parseFile(final File yangFile, final File directory) throws IOException,
-    YangSyntaxErrorException {
+    public SchemaContext parseFile(final File yangFile, final File directory) throws IOException, YangSyntaxErrorException {
         Preconditions.checkState(yangFile.exists(), yangFile + " does not exists");
         Preconditions.checkState(directory.exists(), directory + " does not exists");
         Preconditions.checkState(directory.isDirectory(), directory + " is not a directory");
@@ -194,12 +196,8 @@ public final class YangParserImpl implements YangContextParser {
     }
 
     @Override
-    public SchemaContext parseSources(final Collection<ByteSource> sources) throws IOException,
-    YangSyntaxErrorException {
-        Collection<Module> unsorted = parseYangModelSources(sources).values();
-        Set<Module> sorted = new LinkedHashSet<>(
-                ModuleDependencySort.sort(unsorted.toArray(new Module[unsorted.size()])));
-        return resolveSchemaContext(sorted);
+    public SchemaContext parseSources(final Collection<ByteSource> sources) throws IOException,YangSyntaxErrorException {
+        return assembleContext(parseYangModelSources(sources).values());
     }
 
     @Override
@@ -236,7 +234,7 @@ public final class YangParserImpl implements YangContextParser {
         return resolveSchemaContext(result);
     }
 
-    private LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> resolveModulesWithImports(final List<ModuleBuilder> sorted,
+    private static LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> resolveModulesWithImports(final List<ModuleBuilder> sorted,
             final SchemaContext context) {
         final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = orderModules(sorted);
         for (ModuleBuilder module : sorted) {
@@ -335,8 +333,21 @@ public final class YangParserImpl implements YangContextParser {
         return new SchemaContextImpl(modules, identifiersToSources);
     }
 
-    private Map<ByteSource, Module> parseYangModelSources(final Collection<ByteSource> sources) throws IOException,
-    YangSyntaxErrorException {
+    public Collection<Module> buildModules(final Collection<ModuleBuilder> builders) {
+        List<ModuleBuilder> sorted = ModuleDependencySort.sort(builders);
+        Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModulesWithImports(sorted, null);
+        Map<ModuleBuilder, Module> builderToModule = build(modules);
+
+        return builderToModule.values();
+    }
+
+    public SchemaContext assembleContext(final Collection<Module> modules) {
+        final Set<Module> sorted = new LinkedHashSet<>(
+                ModuleDependencySort.sort(modules.toArray(new Module[modules.size()])));
+        return resolveSchemaContext(sorted);
+    }
+
+    private Map<ByteSource, Module> parseYangModelSources(final Collection<ByteSource> sources) throws IOException, YangSyntaxErrorException {
         if (sources == null || sources.isEmpty()) {
             return Collections.emptyMap();
         }
@@ -368,8 +379,7 @@ public final class YangParserImpl implements YangContextParser {
      * @throws YangSyntaxErrorException
      */
     // TODO: remove ByteSource result after removing YangModelParser
-    private Map<ByteSource, ModuleBuilder> resolveSources(final Collection<ByteSource> streams) throws IOException,
-    YangSyntaxErrorException {
+    private Map<ByteSource, ModuleBuilder> resolveSources(final Collection<ByteSource> streams) throws IOException, YangSyntaxErrorException {
         Map<ByteSource, ModuleBuilder> builders = parseSourcesToBuilders(streams);
         return resolveSubmodules(builders);
     }
@@ -511,7 +521,7 @@ public final class YangParserImpl implements YangContextParser {
      *            topologically sorted modules
      * @return modules ordered by name and revision
      */
-    private LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> orderModules(final List<ModuleBuilder> modules) {
+    private static LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> orderModules(final List<ModuleBuilder> modules) {
         final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> result = new LinkedHashMap<>();
         for (final ModuleBuilder builder : modules) {
             if (builder == null) {
@@ -583,31 +593,29 @@ public final class YangParserImpl implements YangContextParser {
         }
     }
 
-    private Map<ByteSource, ParseTree> parseYangSources(final Collection<ByteSource> sources) throws IOException,
-    YangSyntaxErrorException {
+    private Map<ByteSource, ParseTree> parseYangSources(final Collection<ByteSource> sources) throws IOException, YangSyntaxErrorException {
         final Map<ByteSource, ParseTree> trees = new HashMap<>();
         for (ByteSource source : sources) {
-            trees.put(source, parseYangSource(source));
+            try (InputStream stream = source.openStream()) {
+                trees.put(source, parseYangSource(stream));
+            }
         }
         return trees;
     }
 
-    private YangContext parseYangSource(final ByteSource source) throws IOException, YangSyntaxErrorException {
-        try (InputStream stream = source.openStream()) {
-            final ANTLRInputStream input = new ANTLRInputStream(stream);
-            final YangLexer lexer = new YangLexer(input);
-            final CommonTokenStream tokens = new CommonTokenStream(lexer);
-            final YangParser parser = new YangParser(tokens);
-            parser.removeErrorListeners();
+    public static YangContext parseYangSource(final InputStream stream) throws IOException, YangSyntaxErrorException {
+        final YangLexer lexer = new YangLexer(new ANTLRInputStream(stream));
+        final CommonTokenStream tokens = new CommonTokenStream(lexer);
+        final YangParser parser = new YangParser(tokens);
+        parser.removeErrorListeners();
 
-            final YangErrorListener errorListener = new YangErrorListener();
-            parser.addErrorListener(errorListener);
+        final YangErrorListener errorListener = new YangErrorListener();
+        parser.addErrorListener(errorListener);
 
-            final YangContext result = parser.yang();
-            errorListener.validate();
+        final YangContext result = parser.yang();
+        errorListener.validate();
 
-            return result;
-        }
+        return result;
     }
 
     /**
@@ -1276,5 +1284,4 @@ public final class YangParserImpl implements YangContextParser {
         }
         dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());
     }
-
 }
index fcf0b8924c15fc3ab81a6e908b4d77173683f412..97590779b2a75934a46c8f6d19ad1f015c04ef17 100644 (file)
@@ -11,9 +11,12 @@ import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.ge
 
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableSet;
+
 import java.io.InputStream;
 import java.util.Date;
 import java.util.List;
+
+import org.antlr.v4.runtime.ParserRuleContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Import_stmtContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Include_stmtContext;
@@ -25,6 +28,7 @@ import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtCont
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 
 /**
@@ -53,7 +57,7 @@ public abstract class YangModelDependencyInfo {
     private final ImmutableSet<ModuleImport> dependencies;
 
     YangModelDependencyInfo(final String name, final String formattedRevision,
-                            final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
+            final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
         this.name = name;
         this.formattedRevision = formattedRevision;
         this.revision = QName.parseRevision(formattedRevision);
@@ -142,6 +146,29 @@ public abstract class YangModelDependencyInfo {
         return true;
     }
 
+    /**
+     * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree
+     * of a YANG model.
+     *
+     * @param tree Abstract syntax tree
+     * @return {@link YangModelDependencyInfo}
+     * @throws YangSyntaxErrorException
+     *             If the AST is not a valid YANG module/submodule
+     */
+    public static YangModelDependencyInfo fromAST(final String name, final ParserRuleContext tree) throws YangSyntaxErrorException {
+        final Optional<Module_stmtContext> moduleCtx = getFirstContext(tree, Module_stmtContext.class);
+        if (moduleCtx.isPresent()) {
+            return parseModuleContext(moduleCtx.get());
+        }
+
+        final Optional<Submodule_stmtContext> submoduleCtx = getFirstContext(tree, Submodule_stmtContext.class);
+        if (submoduleCtx.isPresent()) {
+            return parseSubmoduleContext(submoduleCtx.get());
+        }
+
+        throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
+    }
+
     /**
      * Extracts {@link YangModelDependencyInfo} from input stream
      * containing YANG model.
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolver.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolver.java
new file mode 100644 (file)
index 0000000..45ad366
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * 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.yangtools.yang.parser.repo;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Inter-module dependency resolved. Given a set of schema source identifiers and their
+ * corresponding dependency information, the {@link #create(Map)} method creates a
+ * a view of how consistent the dependencies are. In particular, this detects whether
+ * any imports are unsatisfied.
+ *
+ * FIXME: improve this class to track and expose how wildcard imports were resolved.
+ *        That information will allow us to track "damage" to dependency resolution
+ *        as new models are added to a schema context.
+ */
+final class DependencyResolver {
+    private static final Logger LOG = LoggerFactory.getLogger(DependencyResolver.class);
+    private final Collection<SourceIdentifier> resolvedSources;
+    private final Collection<SourceIdentifier> unresolvedSources;
+    private final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports;
+
+    public DependencyResolver(final Collection<SourceIdentifier> resolvedSources,
+            final Collection<SourceIdentifier> unresolvedSources, final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
+        this.resolvedSources = Preconditions.checkNotNull(resolvedSources);
+        this.unresolvedSources = Preconditions.checkNotNull(unresolvedSources);
+        this.unsatisfiedImports = Preconditions.checkNotNull(unsatisfiedImports);
+    }
+
+    private static SourceIdentifier findWildcard(final Iterable<SourceIdentifier> haystack, final String needle) {
+        for (SourceIdentifier r : haystack) {
+            if (r.getName().equals(needle)) {
+                return r;
+            }
+        }
+
+        return null;
+    }
+
+    private static boolean isKnown(final Collection<SourceIdentifier> haystack, final ModuleImport mi) {
+        final String rev = mi.getRevision() != null ? mi.getRevision().toString() : null;
+        final SourceIdentifier msi = SourceIdentifier.create(mi.getModuleName(), Optional.fromNullable(rev));
+
+        // Quick lookup
+        if (haystack.contains(msi)) {
+            return true;
+        }
+
+        // Slow revision-less walk
+        return rev == null && findWildcard(haystack, mi.getModuleName()) != null;
+    }
+
+    public static final DependencyResolver create(final Map<SourceIdentifier, YangModelDependencyInfo> depInfo) {
+        final Collection<SourceIdentifier> resolved = new ArrayList<>(depInfo.size());
+        final Collection<SourceIdentifier> pending = new ArrayList<>(depInfo.keySet());
+
+        boolean progress;
+        do {
+            progress = false;
+
+            final Iterator<SourceIdentifier> it = pending.iterator();
+            while (it.hasNext()) {
+                final SourceIdentifier id = it.next();
+                final YangModelDependencyInfo dep = depInfo.get(id);
+
+                boolean okay = true;
+                for (ModuleImport mi : dep.getDependencies()) {
+                    if (!isKnown(resolved, mi)) {
+                        LOG.debug("Source {} is missing import {}", id, mi);
+                        okay = false;
+                        break;
+                    }
+                }
+
+                if (okay) {
+                    LOG.debug("Resolved source {}", id);
+                    resolved.add(id);
+                    it.remove();
+                    progress = true;
+                }
+            }
+        } while (progress);
+
+        if (!pending.isEmpty()) {
+            final Multimap<SourceIdentifier, ModuleImport> imports = ArrayListMultimap.create();
+            for (SourceIdentifier id : pending) {
+                final YangModelDependencyInfo dep = depInfo.get(id);
+                for (ModuleImport mi : dep.getDependencies()) {
+                    if (!isKnown(pending, mi) && !isKnown(resolved, mi)) {
+                        imports.put(id, mi);
+                    }
+                }
+            }
+
+            return new DependencyResolver(resolved, pending, imports);
+        } else {
+            return new DependencyResolver(resolved, Collections.<SourceIdentifier>emptyList(), ImmutableMultimap.<SourceIdentifier, ModuleImport>of());
+        }
+    }
+
+    /**
+     * Collection of sources which have been resolved.
+     *
+     * @return
+     */
+    Collection<SourceIdentifier> getResolvedSources() {
+        return resolvedSources;
+    }
+
+    /**
+     * Collection of sources which have not been resolved due to missing dependencies.
+     *
+     * @return
+     */
+    Collection<SourceIdentifier> getUnresolvedSources() {
+        return unresolvedSources;
+    }
+
+    /**
+     * Detailed information about which imports were missing. The key in the map
+     * is the source identifier of module which was issuing an import, the values
+     * are imports which were unsatisfied.
+     *
+     * Note that this map contains only imports which are missing from the reactor,
+     * not transitive failures.
+     *
+     * Examples:
+     *
+     * If A imports B, B imports C, and both A and B are in the reactor, only B->C
+     * will be reported.
+     *
+     * If A imports B and C, B imports C, and both A and B are in the reactor,
+     * A->C and B->C will be reported.
+     *
+     * @return
+     */
+    Multimap<SourceIdentifier, ModuleImport> getUnsatisfiedImports() {
+        return unsatisfiedImports;
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLSchemaContextResolver.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLSchemaContextResolver.java
new file mode 100644 (file)
index 0000000..8ee18e5
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * 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.yangtools.yang.parser.repo;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Maps.EntryTransformer;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.annotation.concurrent.GuardedBy;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformationException;
+import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserListenerImpl;
+import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
+import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ThreadSafe
+public class URLSchemaContextResolver {
+    private static final Logger LOG = LoggerFactory.getLogger(URLSchemaContextResolver.class);
+    private static final Function<ASTSchemaSource, YangModelDependencyInfo> EXTRACT_DEPINFO = new Function<ASTSchemaSource, YangModelDependencyInfo>() {
+        @Override
+        public YangModelDependencyInfo apply(final ASTSchemaSource input) {
+            return input.getDependencyInformation();
+        }
+    };
+    private static final EntryTransformer<SourceIdentifier, Collection<YangModelDependencyInfo>, YangModelDependencyInfo> SQUASH_DEPINFO =
+            new EntryTransformer<SourceIdentifier, Collection<YangModelDependencyInfo>, YangModelDependencyInfo>() {
+        @Override
+        public YangModelDependencyInfo transformEntry(final SourceIdentifier key, final Collection<YangModelDependencyInfo> value) {
+            // FIXME: validate that all the info objects are the same
+            return value.iterator().next();
+        }
+    };
+    private static final Function<ASTSchemaSource, ParserRuleContext> EXTRACT_AST = new Function<ASTSchemaSource, ParserRuleContext>() {
+        @Override
+        public ParserRuleContext apply(final ASTSchemaSource input) {
+            return input.getAST();
+        }
+    };
+    private static final EntryTransformer<SourceIdentifier, Collection<ParserRuleContext>, ParserRuleContext> SQUASH_AST =
+            new EntryTransformer<SourceIdentifier, Collection<ParserRuleContext>, ParserRuleContext>() {
+        @Override
+        public ParserRuleContext transformEntry(final SourceIdentifier key, final Collection<ParserRuleContext> value) {
+            // FIXME: validate that all the info objects are the same
+            return value.iterator().next();
+        }
+    };
+
+    @GuardedBy("this")
+    private final Multimap<SourceIdentifier, ASTSchemaSource> resolvedRegs = ArrayListMultimap.create();
+    private final AtomicReference<Optional<SchemaContext>> currentSchemaContext = new AtomicReference<>(Optional.<SchemaContext>absent());
+    private final Queue<URLRegistration> outstandingRegs = new ConcurrentLinkedQueue<>();
+    private final TextToASTTransformer transformer;
+    @GuardedBy("this")
+    private Object version = new Object();
+    @GuardedBy("this")
+    private Object contextVersion = version;
+
+    private final class URLRegistration extends AbstractObjectRegistration<URL> {
+        @GuardedBy("this")
+        private CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> future;
+        @GuardedBy("this")
+        private ASTSchemaSource result;
+
+        protected URLRegistration(final URL url, final CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> future) {
+            super(url);
+            this.future = Preconditions.checkNotNull(future);
+        }
+
+        private synchronized boolean setResult(final ASTSchemaSource result) {
+            if (future != null) {
+                this.result = result;
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        protected void removeRegistration() {
+            // Cancel the future, but it may already be completing
+            future.cancel(false);
+
+            synchronized (this) {
+                future = null;
+                outstandingRegs.remove(this);
+                if (result != null) {
+                    removeSchemaSource(result);
+                }
+            }
+        }
+    }
+
+    private URLSchemaContextResolver(final TextToASTTransformer transformer) {
+        this.transformer = Preconditions.checkNotNull(transformer);
+    }
+
+    public static URLSchemaContextResolver create(final String name) {
+        final ThreadFactory f = new ThreadFactoryBuilder().setDaemon(true).setNameFormat(name + "yangparser-%d").build();
+        final ListeningExecutorService s = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(f));
+
+        return new URLSchemaContextResolver(TextToASTTransformer.create(s));
+    }
+
+    /**
+     * Register a URL hosting a YANG Text file.
+     *
+     * @param url URL
+     */
+    public ObjectRegistration<URL> registerSource(final URL url) {
+        checkArgument(url != null, "Supplied URL must not be null");
+
+        final SourceIdentifier id = SourceIdentifier.create(url.getFile().toString(), Optional.<String>absent());
+        final YangTextSchemaSource text = new YangTextSchemaSource(id) {
+            @Override
+            public InputStream openStream() throws IOException {
+                return url.openStream();
+            }
+
+            @Override
+            protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+                return toStringHelper.add("url", url);
+            }
+        };
+
+        final CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> ast = transformer.transformSchemaSource(text);
+        final URLRegistration reg = new URLRegistration(url, ast);
+        outstandingRegs.add(reg);
+
+        Futures.addCallback(ast, new FutureCallback<ASTSchemaSource>() {
+            @Override
+            public void onSuccess(final ASTSchemaSource result) {
+                LOG.trace("Resolved URL {} to source {}", url, result);
+
+                outstandingRegs.remove(reg);
+                if (reg.setResult(result)) {
+                    addSchemaSource(result);
+                }
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                LOG.warn("Failed to parse YANG text from {}, ignoring it", url, t);
+                outstandingRegs.remove(reg);
+            }
+        });
+
+        return reg;
+    }
+
+    private synchronized void addSchemaSource(final ASTSchemaSource src) {
+        resolvedRegs.put(src.getIdentifier(), src);
+        version = new Object();
+    }
+
+    private synchronized void removeSchemaSource(final ASTSchemaSource src) {
+        resolvedRegs.put(src.getIdentifier(), src);
+        version = new Object();
+    }
+
+    /**
+     * Try to parse all currently available yang files and build new schema context.
+     * @return new schema context iif there is at least 1 yang file registered and
+     *         new schema context was successfully built.
+     */
+    public Optional<SchemaContext> getSchemaContext() {
+        while (true) {
+            Optional<SchemaContext> result;
+            final Multimap<SourceIdentifier, ASTSchemaSource> sources;
+            final Object v;
+            synchronized (this) {
+                result = currentSchemaContext.get();
+                if (version == contextVersion) {
+                    return result;
+                }
+
+                sources = ImmutableMultimap.copyOf(resolvedRegs);
+                v = version;
+            }
+
+            if (!sources.isEmpty()) {
+                final Map<SourceIdentifier, YangModelDependencyInfo> deps =
+                        Maps.transformEntries(Multimaps.transformValues(sources, EXTRACT_DEPINFO).asMap(), SQUASH_DEPINFO);
+
+                LOG.debug("Resolving dependency reactor {}", deps);
+                final DependencyResolver res = DependencyResolver.create(deps);
+                if (!res.getUnresolvedSources().isEmpty()) {
+                    LOG.debug("Omitting models {} due to unsatisfied imports {}", res.getUnresolvedSources(), res.getUnsatisfiedImports());
+                }
+
+                final Map<SourceIdentifier, ParserRuleContext> asts =
+                        Maps.transformEntries(Multimaps.transformValues(sources, EXTRACT_AST).asMap(), SQUASH_AST);
+
+                final ParseTreeWalker walker = new ParseTreeWalker();
+                final Map<SourceIdentifier, ModuleBuilder> sourceToBuilder = new LinkedHashMap<>();
+
+                for (Entry<SourceIdentifier, ParserRuleContext> entry : asts.entrySet()) {
+                    final YangParserListenerImpl yangModelParser = new YangParserListenerImpl(entry.getKey().getName());
+                    walker.walk(yangModelParser, entry.getValue());
+                    ModuleBuilder moduleBuilder = yangModelParser.getModuleBuilder();
+
+                    moduleBuilder.setSource(sources.get(entry.getKey()).iterator().next().getYangText());
+                    sourceToBuilder.put(entry.getKey(), moduleBuilder);
+                }
+                LOG.debug("Modules ready for integration");
+
+                final YangParserImpl parser = YangParserImpl.getInstance();
+                final Collection<Module> modules = parser.buildModules(sourceToBuilder.values());
+                LOG.debug("Integrated cross-references modules");
+
+                result = Optional.of(parser.assembleContext(modules));
+            } else {
+                result = Optional.absent();
+            }
+
+            synchronized (this) {
+                if (v == version) {
+                    currentSchemaContext.set(result);
+                    contextVersion = version;
+                    return result;
+                }
+
+                LOG.debug("Context version {} expected {}, retry", version, v);
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java
new file mode 100644 (file)
index 0000000..ab353a0
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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.yangtools.yang.parser.util;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+import javax.annotation.Nonnull;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
+
+/**
+ * Abstract Syntax Tree representation of a schema source. This representation
+ * is internal to the YANG parser implementation, as it relies on ANTLR types.
+ *
+ * Instances of this representation are used for caching purposes, as they
+ * are a natural intermediate step in YANG text processing pipeline: the text
+ * has been successfully parsed, so we know it is syntactically correct. It also
+ * passes basic semantic validation and we were able to extract dependency
+ * information.
+ */
+public final class ASTSchemaSource implements SchemaSourceRepresentation {
+    private final YangModelDependencyInfo depInfo;
+    private final ParserRuleContext tree;
+    private final SourceIdentifier id;
+    private final String text;
+
+    private ASTSchemaSource(final @Nonnull SourceIdentifier id, @Nonnull final ParserRuleContext tree, final @Nonnull YangModelDependencyInfo depInfo, final @Nonnull String text) {
+        this.depInfo = Preconditions.checkNotNull(depInfo);
+        this.tree = Preconditions.checkNotNull(tree);
+        this.id = Preconditions.checkNotNull(id);
+        this.text = Preconditions.checkNotNull(text);
+    }
+
+    /**
+     * Create a new instance of AST representation for a abstract syntax tree,
+     * performing minimal semantic analysis to acquire dependency information.
+     *
+     * @param name YANG source name. Used only for error reporting.
+     * @param tree ANTLR abstract syntax tree
+     * @return A new representation instance.
+     * @throws YangSyntaxErrorException if we fail to extract dependency information.
+     */
+    public static final ASTSchemaSource create(final @Nonnull String name, final @Nonnull ParserRuleContext tree) throws YangSyntaxErrorException {
+        final YangModelDependencyInfo depInfo = YangModelDependencyInfo.fromAST(name, tree);
+        final SourceIdentifier id = new SourceIdentifier(depInfo.getName(), Optional.of(depInfo.getFormattedRevision()));
+        return new ASTSchemaSource(id, tree, depInfo, null);
+    }
+
+    /**
+     * Create a new instance of AST representation for a abstract syntax tree,
+     * performing minimal semantic analysis to acquire dependency information.
+     *
+     * @param name YANG source name. Used only for error reporting.
+     * @param tree ANTLR abstract syntax tree
+     * @return A new representation instance.
+     * @throws YangSyntaxErrorException if we fail to extract dependency information.
+     *
+     * @deprecated Migration only, will be removed as soon as the migration is completed.
+     */
+    @Deprecated
+    public static final ASTSchemaSource create(final @Nonnull String name, final @Nonnull ParserRuleContext tree, final String text) throws YangSyntaxErrorException {
+        final YangModelDependencyInfo depInfo = YangModelDependencyInfo.fromAST(name, tree);
+        final SourceIdentifier id = new SourceIdentifier(depInfo.getName(), Optional.of(depInfo.getFormattedRevision()));
+        return new ASTSchemaSource(id, tree, depInfo, text);
+    }
+
+
+    @Override
+    public SourceIdentifier getIdentifier() {
+        return id;
+    }
+
+    @Override
+    public Class<? extends SchemaSourceRepresentation> getType() {
+        return ASTSchemaSource.class;
+    }
+
+    /**
+     * Return the underlying abstract syntax tree.
+     *
+     * @return Underlying AST.
+     */
+    public @Nonnull ParserRuleContext getAST() {
+        return tree;
+    }
+
+    /**
+     * Return the dependency information as extracted from the AST.
+     *
+     * FIXME: this method should be extracted into a public interface in the
+     *        model.api.repo class, relying solely on model.api types.
+     *
+     * @return Dependency information.
+     */
+    public @Nonnull YangModelDependencyInfo getDependencyInformation() {
+        return depInfo;
+    }
+
+    /**
+     * Return the semantically-equivalent text YANG text source.
+     *
+     * @return YANG text source
+     * @deprecated Used for migration purposes. Users are advised to use the
+     *             schema repository to acquire the representation of their
+     *             choice. Will be removed as soon as the migration is completed.
+     */
+    @Deprecated
+    public @Nonnull String getYangText() {
+        return text;
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java
new file mode 100644 (file)
index 0000000..ce9d6a8
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * 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.yangtools.yang.parser.util;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.io.CharStreams;
+import com.google.common.io.InputSupplier;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.Callable;
+
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformationException;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformer;
+import org.opendaylight.yangtools.yang.parser.impl.YangModelBasicValidationListener;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link SchemaSourceTransformer} which handles translation of models from
+ * {@link YangTextSchemaSource} representation into {@link ASTSchemaSource}.
+ *
+ * While this class is currently used explicitly, its long-term purpose is to
+ * be registered with a {@link org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry}
+ * and be invoked on demand when the processing pipeline requests the
+ * ASTSchemaSource representation.
+ */
+public final class TextToASTTransformer implements SchemaSourceTransformer<YangTextSchemaSource, ASTSchemaSource> {
+    private static final Logger LOG = LoggerFactory.getLogger(TextToASTTransformer.class);
+    private static final Function<Exception, SchemaSourceTransformationException> MAPPER = new ExceptionMapper<SchemaSourceTransformationException>("Source transformation", SchemaSourceTransformationException.class) {
+        @Override
+        protected SchemaSourceTransformationException newWithCause(final String message, final Throwable cause) {
+            return new SchemaSourceTransformationException(message, cause);
+        }
+    };
+
+    private final ListeningExecutorService executor;
+
+    private TextToASTTransformer(final ListeningExecutorService executor) {
+        this.executor = Preconditions.checkNotNull(executor);
+    }
+
+    public static final TextToASTTransformer create(final ListeningExecutorService executor) {
+        return new TextToASTTransformer(executor);
+    }
+
+    @Override
+    public Class<YangTextSchemaSource> getInputRepresentation() {
+        return YangTextSchemaSource.class;
+    }
+
+    @Override
+    public Class<ASTSchemaSource> getOutputRepresentation() {
+        return ASTSchemaSource.class;
+    }
+
+    @Override
+    public CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> transformSchemaSource(final YangTextSchemaSource source) {
+        return Futures.makeChecked(executor.submit(new Callable<ASTSchemaSource>() {
+            @Override
+            public ASTSchemaSource call() throws IOException, YangSyntaxErrorException {
+                try (InputStream is = source.openStream()) {
+                    final YangContext ctx = YangParserImpl.parseYangSource(is);
+                    LOG.debug("Model {} parsed successfully", source);
+
+                    final ParseTreeWalker walker = new ParseTreeWalker();
+                    final YangModelBasicValidationListener validator = new YangModelBasicValidationListener();
+                    walker.walk(validator, ctx);
+                    LOG.debug("Model {} validated successfully", source);
+
+                    // Backwards compatibility
+                    final String text = CharStreams.toString(
+                            CharStreams.newReaderSupplier(new InputSupplier<InputStream>() {
+                                @Override
+                                public InputStream getInput() throws IOException {
+                                    return source.openStream();
+                                }
+                            }, Charsets.UTF_8));
+
+                    return ASTSchemaSource.create(source.getIdentifier().getName(), ctx, text);
+                }
+            }
+        }), MAPPER);
+    }
+
+    @Override
+    public int getCost() {
+        // We perform a direct translation, so the cost is 1.
+        return 1;
+    }
+}
index 050f62092877a093f389eb8b12dca545749c1bc6..022cda45a59eae835f5989e2597f6e492bcbc2ad 100644 (file)
@@ -16,6 +16,7 @@ import static org.junit.Assert.assertTrue;
 import java.io.File;
 import java.math.BigInteger;
 import java.net.URI;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
 import org.junit.Before;
@@ -333,4 +334,13 @@ public class TypesResolutionTest {
         assertNotNull(type);
     }
 
+    @Test
+    public void testUnionWithExt() throws Exception {
+        File extdef = new File(getClass().getResource("/types/union-with-ext/extdef.yang").toURI());
+        File unionbug = new File(getClass().getResource("/types/union-with-ext/unionbug.yang").toURI());
+        File inet = new File(getClass().getResource("/ietf/ietf-inet-types@2010-09-24.yang").toURI());
+        YangContextParser parser = new YangParserImpl();
+        parser.parseFiles(Arrays.asList(extdef, unionbug, inet));
+    }
+
 }
diff --git a/yang/yang-parser-impl/src/test/resources/types/union-with-ext/extdef.yang b/yang/yang-parser-impl/src/test/resources/types/union-with-ext/extdef.yang
new file mode 100644 (file)
index 0000000..68a568b
--- /dev/null
@@ -0,0 +1,13 @@
+module "extdef" {
+    yang-version 1;
+    namespace "urn:test:bug:extdef";
+    prefix "extdef";
+
+    revision 2012-04-16 {
+    }
+
+    extension "help" {
+        argument "text";
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/test/resources/types/union-with-ext/unionbug.yang b/yang/yang-parser-impl/src/test/resources/types/union-with-ext/unionbug.yang
new file mode 100644 (file)
index 0000000..a882168
--- /dev/null
@@ -0,0 +1,31 @@
+module unionbug {
+    yang-version 1;
+    namespace "urn:test:bug:unionbug";
+    prefix "unionbug";
+
+    import extdef {
+        prefix extdef;
+    }
+
+    import ietf-inet-types {
+        prefix "inet";
+    }
+
+    revision 2012-04-16 {
+    }
+
+    typedef address {
+        type union {
+            type inet:ip-address {
+                extdef:help "IP address";
+            }
+            type inet:ip-prefix {
+                extdef:help "Subnet";
+            }
+            type string {
+                extdef:help "Address name";
+            }
+        }
+    }
+
+}