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
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
+++ /dev/null
-/*
- * 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;
- }
- }
-}
+++ /dev/null
-/*
- * 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
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;
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.");
}
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);
}
</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>
</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>
<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>
--- /dev/null
+/*
+ * 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;
+ }
+}
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;
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);
}
}
}
}
/**
- * 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.
* @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) {
}
/**
- * 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();
+ }
+ }
}
* 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;
--- /dev/null
+/*
+ * 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<>();
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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 );
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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 );
+ }
+ }
+ }
+}
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.
*
* @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.
* @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) {
try {
task.run();
} finally {
- deadlockDetector.set(null);
+ deadlockDetector.remove();
}
}
};
try {
return delagate.call();
} finally {
- deadlockDetector.set(null);
+ deadlockDetector.remove();
}
}
};
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
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 );
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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" );
+ }
+ };
+}
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;
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.
*/
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 );
};
@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>() {
@Test
public void testNonBlockingSubmitOnExecutorThread() throws Throwable {
+ executor = newExecutor();
+
testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_CALLABLE );
testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE );
testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE_WITH_RESULT );
@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();
@Test
public void testBlockingSubmitOnExecutorThread() throws Exception {
+ executor = newExecutor();
+
testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_CALLABLE );
testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE );
testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE_WITH_RESULT );
public void run() {
try {
- invoker.invokeExecutor( executor ).get();
+ invoker.invokeExecutor( executor, null ).get();
} catch( ExecutionException e ) {
caughtEx.set( e.getCause() );
} catch( Throwable e ) {
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();
+ }
+ }
}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+ }
+ }
+ }
+}
<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>
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 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);
}
/**
- *
- * 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");
}
}
- /**
- *
- * 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");
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;
}
</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>
--- /dev/null
+/*
+ * 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;
+ }
+}
* 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;
* 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;
* 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;
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;
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) {
mapNodeCnSnParser = new MapNodeCnSnParser(mapEntryNodeCnSnParser);
choiceNodeCnSnParser = new ChoiceNodeCnSnParser(dispatcher);
augmentationNodeCnSnParser = new AugmentationNodeCnSnParser(dispatcher);
+
}
public static CnSnToNormalizedNodeParserFactory getInstance() {
public ToNormalizedNodeParser<Node<?>, MapEntryNode, ListSchemaNode> getMapEntryNodeParser() {
return mapEntryNodeCnSnParser;
}
+
+ @Override
+ public ToNormalizedNodeParser<Node<?>, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeParser() {
+ return anyXmlNodeCnSnParser;
+ }
}
* 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;
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;
* 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;
* 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;
* 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;
* 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;
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;
* 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;
--- /dev/null
+/*
+ * 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();
+ }
+}
* 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;
* 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;
* 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;
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;
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<?>>(
choiceSerializer = new ChoiceNodeCnSnSerializer(dispatcher);
augmentSerializer = new AugmentationNodeCnSnSerializer(dispatcher);
leafNodeSerializer = new LeafNodeCnSnSerializer();
+ anyXmlNodeSerializer = new AnyXmlNodeCnSnSerializer();
leafSetEntryNodeSerializer = new LeafSetEntryNodeCnSnSerializer();
leafSetSerializer = new LeafSetNodeCnSnSerializer(leafSetEntryNodeSerializer);
public FromNormalizedNodeSerializer<Node<?>, MapEntryNode, ListSchemaNode> getMapEntryNodeSerializer() {
return mapEntryNodeSerializer;
}
+
+ @Override
+ public FromNormalizedNodeSerializer<Node<?>, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeSerializer() {
+ return anyXmlNodeSerializer;
+ }
}
* 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<?>> {
}
@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
* 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;
* 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;
* 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;
* 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<?>> {
}
@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
* 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;
* 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;
* 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;
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;
* /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();
}
/**
.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"));
.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"));
.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;
* 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;
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;
* 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;
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;
"lf1":"lf1",
"lf11":"value from case (cs1)"
+ "anxml-composite": {
+ "anxml-cont" {
+ }
+ }
+
+ "anxml-simple":43;
}
}
\ No newline at end of file
}
}
+ anyxml anxml-composite;
+ anyxml anxml-simple;
}
}
*/
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;
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;
FromNormalizedNodeSerializer<E, LeafSetNode<?>, LeafListSchemaNode> getLeafSetNodeSerializer();
FromNormalizedNodeSerializer<E, MapEntryNode, ListSchemaNode> getMapEntryNodeSerializer();
FromNormalizedNodeSerializer<E, MapNode, ListSchemaNode> getMapNodeSerializer();
+ FromNormalizedNodeSerializer<E, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeSerializer();
}
*/
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;
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;
ToNormalizedNodeParser<E, LeafSetNode<?>, LeafListSchemaNode> getLeafSetNodeParser();
ToNormalizedNodeParser<E, MapEntryNode, ListSchemaNode> getMapEntryNodeParser();
ToNormalizedNodeParser<E, MapNode, ListSchemaNode> getMapNodeParser();
+ ToNormalizedNodeParser<E, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeParser();
}
--- /dev/null
+/*
+ * 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);
+
+}
*/
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;
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.
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());
--- /dev/null
+/*
+ * 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);
+}
*/
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;
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> {
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;
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);
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);
*/
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;
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;
public ToNormalizedNodeParser<Element, MapNode, ListSchemaNode> getMapNodeParser() {
return mapNodeParser;
}
+
+ @Override
+ public ToNormalizedNodeParser<Element, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeParser() {
+ throw new UnsupportedOperationException();
+ }
}
*/
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;
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;
return mapNodeSerializer;
}
+ @Override
+ public FromNormalizedNodeSerializer<Element, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeSerializer() {
+ throw new UnsupportedOperationException();
+ }
+
}
<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>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin-spi</artifactId>
- <version>${it-project.version}</version>
+ <version>${project.version}</version>
</dependency>
</dependencies>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${it-project.version}</version>
+ <version>${project.version}</version>
<executions>
<execution>
<goals>
<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>
<parent>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>test-parent</artifactId>
- <version>1.0</version>
+ <version>0.6.2-SNAPSHOT</version>
</parent>
<artifactId>correct</artifactId>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin-spi</artifactId>
- <version>${it-project.version}</version>
+ <version>${project.version}</version>
</dependency>
</dependencies>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${it-project.version}</version>
+ <version>${project.version}</version>
<executions>
<execution>
<goals>
<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>
<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>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${it-project.version}</version>
+ <version>${project.version}</version>
<executions>
<execution>
<goals>
<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>
<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>
<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>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${it-project.version}</version>
+ <version>${project.version}</version>
<executions>
<execution>
<goals>
<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>
<parent>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>test-parent</artifactId>
- <version>1.0</version>
+ <version>0.6.2-SNAPSHOT</version>
</parent>
<artifactId>generator</artifactId>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${it-project.version}</version>
+ <version>${project.version}</version>
<executions>
<execution>
<goals>
<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>
<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>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${it-project.version}</version>
+ <version>${project.version}</version>
<executions>
<execution>
<goals>
<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>
<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>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${it-project.version}</version>
+ <version>${project.version}</version>
<executions>
<execution>
<goals>
<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>
<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>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${it-project.version}</version>
+ <version>${project.version}</version>
<executions>
<execution>
<goals>
<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>
<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>
<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>
<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>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${it-project.version}</version>
+ <version>${project.version}</version>
<executions>
<execution>
<goals>
<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>
<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>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${it-project.version}</version>
+ <version>${project.version}</version>
<executions>
<execution>
<goals>
<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>
<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>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${it-project.version}</version>
+ <version>${project.version}</version>
<executions>
<execution>
<goals>
<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>
<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>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${it-project.version}</version>
+ <version>${project.version}</version>
<executions>
<execution>
<goals>
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>
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
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();
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;
* 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<>();
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;
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;
}
@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");
}
@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
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) {
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();
}
* @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);
}
* 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) {
}
}
- 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;
}
/**
}
dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());
}
-
}
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;
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;
/**
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);
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.
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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();
+
+ // FIXME: do we need to lug this around?
+ // moduleBuilder.setSource(source);
+ 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);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 ASTSchemaSource(final @Nonnull SourceIdentifier id, @Nonnull final ParserRuleContext tree, final @Nonnull YangModelDependencyInfo depInfo) {
+ this.depInfo = Preconditions.checkNotNull(depInfo);
+ this.tree = Preconditions.checkNotNull(tree);
+ this.id = Preconditions.checkNotNull(id);
+ }
+
+ /**
+ * 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);
+ }
+
+ @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;
+ }
+}
--- /dev/null
+/*
+ * 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.Function;
+import com.google.common.base.Preconditions;
+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);
+
+ return ASTSchemaSource.create(source.getIdentifier().getName(), ctx);
+ }
+ }
+ }), MAPPER);
+ }
+
+ @Override
+ public int getCost() {
+ // We perform a direct translation, so the cost is 1.
+ return 1;
+ }
+}
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;
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));
+ }
+
}
--- /dev/null
+module "extdef" {
+ yang-version 1;
+ namespace "urn:test:bug:extdef";
+ prefix "extdef";
+
+ revision 2012-04-16 {
+ }
+
+ extension "help" {
+ argument "text";
+ }
+
+}
--- /dev/null
+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";
+ }
+ }
+ }
+
+}