import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.concepts.Mutable;
-public class Immutables {
+public final class Immutables {
private Immutables() {
throw new UnsupportedOperationException("Helper class");
public static final Set<Class<?>> KNOWN_IMMUTABLES = Immutables.<Class<?>> asHashSet(
//
Integer.class, Short.class, BigDecimal.class, BigInteger.class, Byte.class, Character.class, Double.class,
- Float.class);
+ Float.class, String.class);
/**
* Determines if object is known to be immutable
* Reference to check
* @return true if object is known to be immutable false otherwise.
*/
- public static boolean isImmutable(Object o) {
+ public static boolean isImmutable(final Object o) {
if (o == null) {
throw new IllegalArgumentException("Object should not be null");
}
}
@SafeVarargs
- private static <E> Set<E> asHashSet(E... list) {
+ private static <E> Set<E> asHashSet(final E... list) {
HashSet<E> ret = new HashSet<>();
for (E e : list) {
ret.add(e);
<extensions>true</extensions>
<configuration>
<instructions>
- <Export-Package>org.opendaylight.yangtools.util</Export-Package>
+ <Export-Package>
+ org.opendaylight.yangtools.util,
+ org.opendaylight.yangtools.util.concurrent
+ </Export-Package>
<Embed-Dependency>java-concurrent-hash-trie-map;inline=true</Embed-Dependency>
</instructions>
</configuration>
--- /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 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.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * 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.
+ * <p>
+ * Deadlock scenarios are most apt to occur with a backing single-threaded executor where setting of
+ * the Future's result is executed on the single thread. Here's a scenario:
+ * <ul>
+ * <li>Client code is currently executing in an executor's single thread.</li>
+ * <li>The client submits another task to the same executor.</li>
+ * <li>The client calls <code>get()</code> synchronously on the returned Future</li>
+ * </ul>
+ * The second submitted task will never execute since the single thread is currently executing
+ * the client code which is blocked waiting for the submitted task to complete. Thus, deadlock has
+ * occurred.
+ * <p>
+ * This class prevents this scenario via the use of a ThreadLocal variable. When a task is invoked,
+ * the ThreadLocal is set and, when a task completes, the ThreadLocal is cleared. Futures returned
+ * from this class override the <code>get</code> methods to check if the ThreadLocal is set. If it is,
+ * an ExecutionException is thrown with a custom cause.
+ *
+ * @author Thomas Pantelis
+ */
+public class DeadlockDetectingListeningExecutorService extends AbstractListeningExecutorService {
+ private final ThreadLocal<Boolean> deadlockDetector = new ThreadLocal<>();
+ private final Function<Void, Exception> deadlockExceptionFunction;
+ private final ExecutorService delegate;
+
+ /**
+ * 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.
+ */
+ 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();
+ }
+
+ @Override
+ public void shutdown() {
+ delegate.shutdown();
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ return delegate.shutdownNow();
+ }
+
+ @Override
+ public void execute(final Runnable command) {
+ delegate.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);
+ }
+
+ @Override
+ public ListenableFuture<?> submit( final Runnable task ) {
+ ListenableFutureTask<Void> futureTask = ListenableFutureTask.create(wrapRunnable(task), null);
+ delegate.execute(futureTask);
+ return wrapListenableFuture(futureTask);
+ }
+
+ @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);
+ }
+
+ private Runnable wrapRunnable(final Runnable task) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ deadlockDetector.set(Boolean.TRUE);
+ try {
+ task.run();
+ } finally {
+ deadlockDetector.set(null);
+ }
+ }
+ };
+ }
+
+ private <T> Callable<T> wrapCallable(final Callable<T> delagate) {
+ return new Callable<T>() {
+ @Override
+ public T call() throws Exception {
+ deadlockDetector.set(Boolean.TRUE);
+ try {
+ return delagate.call();
+ } finally {
+ deadlockDetector.set(null);
+ }
+ }
+ };
+ }
+
+ 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.
+ */
+ return new ForwardingListenableFuture.SimpleForwardingListenableFuture<T>(delegate) {
+ @Override
+ public T get() throws InterruptedException, ExecutionException {
+ checkDeadLockDetectorTL();
+ return super.get();
+ }
+
+ @Override
+ public T get(final long timeout, final TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ checkDeadLockDetectorTL();
+ return super.get(timeout, unit);
+ }
+
+ void checkDeadLockDetectorTL() throws ExecutionException {
+ if (deadlockDetector.get() != null) {
+ throw new ExecutionException("A potential deadlock was detected.",
+ deadlockExceptionFunction.apply(null));
+ }
+ }
+ };
+ }
+}
--- /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 com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Utility exception mapper which translates an Exception to a specified type of Exception.
+ *
+ * This mapper is intended to be used with {@link com.google.common.util.concurrent.Futures#makeChecked(com.google.common.util.concurrent.ListenableFuture, Function)}
+ * <ul>
+ * <li>if exception is the specified type or one of its subclasses, it returns original exception.
+ * <li>if exception is {@link ExecutionException} and the cause is of the specified type, it returns the cause
+ * <li>otherwise returns an instance of the specified exception type with original exception as the cause.
+ * </ul>
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <X> the exception type
+ */
+public abstract class ExceptionMapper<X extends Exception> implements Function<Exception, X> {
+ private final Class<X> exceptionType;
+ private final String opName;
+
+ /**
+ * Constructor.
+ *
+ * @param opName the String prefix for exception messages.
+ * @param exceptionType the exception type to which to translate.
+ */
+ public ExceptionMapper(final String opName, final Class<X> exceptionType) {
+ this.exceptionType = Preconditions.checkNotNull(exceptionType);
+ this.opName = Preconditions.checkNotNull(opName);
+ }
+
+ /**
+ * Invoked to create a new exception instance of the specified type.
+ *
+ * @param message the message for the new exception.
+ * @param cause the cause for the new exception.
+ *
+ * @return an instance of the exception type.
+ */
+ protected abstract X newWithCause(String message, Throwable cause);
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public X apply(final Exception e) {
+
+ // If exception is of the specified type,return it.
+ if (exceptionType.isAssignableFrom( e.getClass())) {
+ return (X) e;
+ }
+
+ // If exception is ExecutionException whose cause is of the specified
+ // type, return the cause.
+ if (e instanceof ExecutionException && e.getCause() != null) {
+ if (exceptionType.isAssignableFrom( e.getCause().getClass())) {
+ return (X) e.getCause();
+ } else {
+ return newWithCause(opName + " execution failed", e.getCause());
+ }
+ }
+
+ // Otherwise return an instance of the specified type with the original
+ // cause.
+
+ if (e instanceof InterruptedException) {
+ return newWithCause( opName + " was interupted.", e);
+ }
+
+ if (e instanceof CancellationException ) {
+ return newWithCause( opName + " was cancelled.", e);
+ }
+
+ // We really shouldn't get here but need to cover it anyway for completeness.
+ return newWithCause(opName + " encountered an unexpected failure", 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.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.AbstractCheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * An implementation of CheckedFuture that provides similar behavior for the <code>get</code> methods
+ * that the <code>checkedGet</code> methods provide.
+ * <p>
+ * For {@link CancellationException} and {@link InterruptedException}, the specified exception mapper
+ * is invoked to translate them to the checked exception type.
+ * <p>
+ * For {@link ExecutionException}, the mapper is invoked to translate the cause to the checked exception
+ * and a new ExecutionException is thrown with the translated cause.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <V> The result type returned by this Future's get method
+ * @param <X> The checked exception type
+ */
+public class MappingCheckedFuture<V, X extends Exception> extends AbstractCheckedFuture<V, X> {
+
+ private final Function<Exception, X> mapper;
+
+ private MappingCheckedFuture( ListenableFuture<V> delegate, Function<Exception, X> mapper ) {
+ super( delegate );
+ this.mapper = Preconditions.checkNotNull( mapper );
+ }
+
+ /**
+ * Creates a new <code>MappingCheckedFuture</code> that wraps the given {@link ListenableFuture}
+ * delegate.
+ *
+ * @param delegate the {@link ListenableFuture} to wrap
+ * @param mapper the mapping {@link Function} used to translate exceptions from the delegate
+ * @return a new <code>MappingCheckedFuture</code>
+ */
+ public static <V, X extends Exception> MappingCheckedFuture<V, X> create(
+ ListenableFuture<V> delegate, Function<Exception, X> mapper ) {
+ return new MappingCheckedFuture<V, X>( delegate, mapper );
+ }
+
+ @Override
+ protected X mapException( Exception e ) {
+ return mapper.apply( e );
+ }
+
+ private ExecutionException wrapInExecutionException( String message, final Exception e ) {
+ return new ExecutionException( message, mapException( e ) );
+ }
+
+ @Override
+ public V get() throws InterruptedException, ExecutionException {
+ try {
+ return super.get();
+ } catch( InterruptedException e ) {
+ Thread.currentThread().interrupt();
+ throw wrapInExecutionException( "Operation was interrupted", e );
+ } catch( CancellationException e ) {
+ throw wrapInExecutionException( "Operation was cancelled", e );
+ } catch( ExecutionException e ) {
+ throw wrapInExecutionException( e.getMessage(), e );
+ }
+ }
+
+ @Override
+ public V get( long timeout, TimeUnit unit )
+ throws InterruptedException, ExecutionException, TimeoutException {
+ try {
+ return super.get( timeout, unit );
+ } catch( InterruptedException e ) {
+ Thread.currentThread().interrupt();
+ throw wrapInExecutionException( "Operation was interrupted", e );
+ } catch( CancellationException e ) {
+ throw wrapInExecutionException( "Operation was cancelled", e );
+ } catch( ExecutionException e ) {
+ throw wrapInExecutionException( e.getMessage(), 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 static org.junit.Assert.*;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+/**
+ * Unit tests for DeadlockDetectingListeningExecutorService.
+ *
+ * @author Thomas Pantelis
+ */
+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 );
+ };
+
+ static final InitialInvoker SUBMIT = new InitialInvoker() {
+ @Override
+ public void invokeExecutor( ListeningExecutorService executor, Runnable task ) {
+ executor.submit( task );
+ }
+ };
+
+ static final InitialInvoker EXECUTE = new InitialInvoker() {
+ @Override
+ public void invokeExecutor( ListeningExecutorService executor, Runnable task ) {
+ executor.execute( task );
+ }
+ };
+
+ @SuppressWarnings("serial")
+ public static class TestDeadlockException extends Exception {
+ }
+
+ public static Function<Void, Exception> DEADLOCK_EXECUTOR_FUNCTION = new Function<Void, Exception>() {
+ @Override
+ public Exception apply( Void notUsed ) {
+ return new TestDeadlockException();
+ }
+ };
+
+ DeadlockDetectingListeningExecutorService executor;
+
+ @Before
+ public void setup() {
+ executor = new DeadlockDetectingListeningExecutorService( Executors.newSingleThreadExecutor(),
+ DEADLOCK_EXECUTOR_FUNCTION );
+ }
+
+ @Test
+ public void testBlockingSubmitOffExecutor() throws Exception {
+
+ // Test submit with Callable.
+
+ ListenableFuture<String> future = executor.submit( new Callable<String>() {
+ @Override
+ public String call() throws Exception{
+ return "foo";
+ }
+ } );
+
+ assertEquals( "Future result", "foo", future.get( 5, TimeUnit.SECONDS ) );
+
+ // Test submit with Runnable.
+
+ executor.submit( new Runnable() {
+ @Override
+ public void run(){
+ }
+ } ).get();
+
+ // Test submit with Runnable and value.
+
+ future = executor.submit( new Runnable() {
+ @Override
+ public void run(){
+ }
+ }, "foo" );
+
+ assertEquals( "Future result", "foo", future.get( 5, TimeUnit.SECONDS ) );
+ }
+
+ @Test
+ public void testNonBlockingSubmitOnExecutorThread() throws Throwable {
+
+ testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_CALLABLE );
+ testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE );
+ testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE_WITH_RESULT );
+
+ testNonBlockingSubmitOnExecutorThread( EXECUTE, SUBMIT_CALLABLE );
+ }
+
+ void testNonBlockingSubmitOnExecutorThread( InitialInvoker initialInvoker,
+ final Invoker invoker ) throws Throwable {
+
+ final AtomicReference<Throwable> caughtEx = new AtomicReference<>();
+ final CountDownLatch futureCompletedLatch = new CountDownLatch( 1 );
+
+ Runnable task = new Runnable() {
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
+ public void run() {
+
+ Futures.addCallback( invoker.invokeExecutor( executor ), new FutureCallback() {
+ @Override
+ public void onSuccess( Object result ) {
+ futureCompletedLatch.countDown();
+ }
+
+ @Override
+ public void onFailure( Throwable t ) {
+ caughtEx.set( t );
+ futureCompletedLatch.countDown();
+ }
+ } );
+ }
+
+ };
+
+ initialInvoker.invokeExecutor( executor, task );
+
+ assertTrue( "Task did not complete - executor likely deadlocked",
+ futureCompletedLatch.await( 5, TimeUnit.SECONDS ) );
+
+ if( caughtEx.get() != null ) {
+ throw caughtEx.get();
+ }
+ }
+
+ @Test
+ public void testBlockingSubmitOnExecutorThread() throws Exception {
+
+ testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_CALLABLE );
+ testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE );
+ testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE_WITH_RESULT );
+
+ testBlockingSubmitOnExecutorThread( EXECUTE, SUBMIT_CALLABLE );
+ }
+
+ void testBlockingSubmitOnExecutorThread( InitialInvoker initialInvoker,
+ final Invoker invoker ) throws Exception {
+
+ final AtomicReference<Throwable> caughtEx = new AtomicReference<>();
+ final CountDownLatch latch = new CountDownLatch( 1 );
+
+ Runnable task = new Runnable() {
+ @Override
+ public void run() {
+
+ try {
+ invoker.invokeExecutor( executor ).get();
+ } catch( ExecutionException e ) {
+ caughtEx.set( e.getCause() );
+ } catch( Throwable e ) {
+ caughtEx.set( e );
+ } finally {
+ latch.countDown();
+ }
+ }
+
+ };
+
+ initialInvoker.invokeExecutor( executor, task );
+
+ assertTrue( "Task did not complete - executor likely deadlocked",
+ latch.await( 5, TimeUnit.SECONDS ) );
+
+ assertNotNull( "Expected exception thrown", caughtEx.get() );
+ assertEquals( "Caught exception type", TestDeadlockException.class, caughtEx.get().getClass() );
+ }
+}
--- /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 static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.Test;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.SettableFuture;
+
+/**
+ * Unit tests for MappingCheckedFuture.
+ *
+ * @author Thomas Pantelis
+ */
+public class MappingCheckedFutureTest {
+
+ interface FutureInvoker {
+ void invokeGet( CheckedFuture<?,?> future ) throws Exception;
+ Throwable extractWrappedTestEx( Exception from );
+ }
+
+ @SuppressWarnings("serial")
+ static class TestException extends Exception {
+ TestException( String message, Throwable cause ) {
+ super( message, cause );
+ }
+ }
+
+ static final ExceptionMapper<TestException> MAPPER = new ExceptionMapper<TestException>(
+ "Test", TestException.class ) {
+
+ @Override
+ protected TestException newWithCause( String message, Throwable cause ) {
+ return new TestException( message, cause );
+ }
+ };
+
+ static final FutureInvoker GET = new FutureInvoker() {
+ @Override
+ public void invokeGet( CheckedFuture<?,?> future ) throws Exception {
+ future.get();
+ }
+
+ @Override
+ public Throwable extractWrappedTestEx( Exception from ) {
+ if( from instanceof ExecutionException ) {
+ return ((ExecutionException)from).getCause();
+ }
+
+ return from;
+ }
+ };
+
+ static final FutureInvoker TIMED_GET = new FutureInvoker() {
+ @Override
+ public void invokeGet( CheckedFuture<?,?> future ) throws Exception {
+ future.get( 1, TimeUnit.HOURS );
+ }
+
+ @Override
+ public Throwable extractWrappedTestEx( Exception from ) {
+ if( from instanceof ExecutionException ) {
+ return ((ExecutionException)from).getCause();
+ }
+
+ return from;
+ }
+ };
+
+ static final FutureInvoker CHECKED_GET = new FutureInvoker() {
+ @Override
+ public void invokeGet( CheckedFuture<?,?> future ) throws Exception {
+ future.checkedGet();
+ }
+
+ @Override
+ public Throwable extractWrappedTestEx( Exception from ) {
+ return from;
+ }
+ };
+
+ static final FutureInvoker TIMED_CHECKED_GET = new FutureInvoker() {
+ @Override
+ public void invokeGet( CheckedFuture<?,?> future ) throws Exception {
+ future.checkedGet( 50, TimeUnit.MILLISECONDS );
+ }
+
+ @Override
+ public Throwable extractWrappedTestEx( Exception from ) {
+ return from;
+ }
+ };
+
+ @Test
+ public void testGet() throws Exception {
+
+ SettableFuture<String> delegate = SettableFuture.create();
+ MappingCheckedFuture<String,TestException> future = MappingCheckedFuture.create( delegate, MAPPER );
+ delegate.set( "test" );
+ assertEquals( "get", "test", future.get() );
+ }
+
+ @Test
+ public void testGetWithExceptions() throws Exception {
+
+ testExecutionException( GET, new RuntimeException() );
+ testExecutionException( GET, new TestException( "mock", null ) );
+ testCancellationException( GET );
+ testInterruptedException( GET );
+ }
+
+ @Test
+ public void testTimedGet() throws Exception {
+
+ SettableFuture<String> delegate = SettableFuture.create();
+ MappingCheckedFuture<String,TestException> future = MappingCheckedFuture.create( delegate, MAPPER );
+ delegate.set( "test" );
+ assertEquals( "get", "test", future.get( 50, TimeUnit.MILLISECONDS ) );
+ }
+
+ @Test
+ public void testTimedGetWithExceptions() throws Exception {
+
+ testExecutionException( TIMED_GET, new RuntimeException() );
+ testCancellationException( TIMED_GET );
+ testInterruptedException( TIMED_GET );
+ }
+
+ @Test
+ public void testCheckedGetWithExceptions() throws Exception {
+
+ testExecutionException( CHECKED_GET, new RuntimeException() );
+ testCancellationException( CHECKED_GET );
+ testInterruptedException( CHECKED_GET );
+ }
+
+ @Test
+ public void testTimedCheckedWithExceptions() throws Exception {
+
+ testExecutionException( TIMED_CHECKED_GET, new RuntimeException() );
+ testCancellationException( TIMED_CHECKED_GET );
+ testInterruptedException( TIMED_CHECKED_GET );
+ }
+
+ private void testExecutionException( FutureInvoker invoker, Throwable cause ) {
+
+ SettableFuture<String> delegate = SettableFuture.create();
+ MappingCheckedFuture<String,TestException> mappingFuture =
+ MappingCheckedFuture.create( delegate, MAPPER );
+
+ delegate.setException( cause );
+
+ try {
+ invoker.invokeGet( mappingFuture );
+ fail( "Expected exception thrown" );
+ } catch( Exception e ) {
+ Throwable expectedTestEx = invoker.extractWrappedTestEx( e );
+ assertNotNull( "Expected returned exception is null", expectedTestEx );
+ assertEquals( "Exception type", TestException.class, expectedTestEx.getClass() );
+
+ if( cause instanceof TestException ) {
+ assertNull( "Expected null cause", expectedTestEx.getCause() );
+ } else {
+ assertSame( "TestException cause", cause, expectedTestEx.getCause() );
+ }
+ }
+ }
+
+ private void testCancellationException( FutureInvoker invoker ) {
+
+ SettableFuture<String> delegate = SettableFuture.create();
+ MappingCheckedFuture<String,TestException> mappingFuture =
+ MappingCheckedFuture.create( delegate, MAPPER );
+
+ mappingFuture.cancel( false );
+
+ try {
+ invoker.invokeGet( mappingFuture );
+ fail( "Expected exception thrown" );
+ } catch( Exception e ) {
+ Throwable expectedTestEx = invoker.extractWrappedTestEx( e );
+ assertNotNull( "Expected returned exception is null", expectedTestEx );
+ assertEquals( "Exception type", TestException.class, expectedTestEx.getClass() );
+ assertEquals( "TestException cause type", CancellationException.class,
+ expectedTestEx.getCause().getClass() );
+ }
+ }
+
+ private void testInterruptedException( final FutureInvoker invoker ) throws Exception {
+
+ SettableFuture<String> delegate = SettableFuture.create();
+ final MappingCheckedFuture<String,TestException> mappingFuture =
+ MappingCheckedFuture.create( delegate, MAPPER );
+
+ final AtomicReference<AssertionError> assertError = new AtomicReference<>();
+ final CountDownLatch doneLatch = new CountDownLatch( 1 );
+ Thread thread = new Thread() {
+
+ @Override
+ public void run() {
+ try {
+ doInvoke();
+ } catch( AssertionError e ) {
+ assertError.set( e );
+ } finally {
+ doneLatch.countDown();
+ }
+ }
+
+ void doInvoke() {
+ try {
+ invoker.invokeGet( mappingFuture );
+ fail( "Expected exception thrown" );
+ } catch( Exception e ) {
+ Throwable expectedTestEx = invoker.extractWrappedTestEx( e );
+ assertNotNull( "Expected returned exception is null", expectedTestEx );
+ assertEquals( "Exception type", TestException.class, expectedTestEx.getClass() );
+ assertEquals( "TestException cause type", InterruptedException.class,
+ expectedTestEx.getCause().getClass() );
+ }
+ }
+ };
+ thread.start();
+
+ thread.interrupt();
+ assertEquals( "get call completed", true, doneLatch.await( 5, TimeUnit.SECONDS ) );
+
+ if( assertError.get() != null ) {
+ throw assertError.get();
+ }
+ }
+}
/*
-* 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
-*/
+ * 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.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.DatastoreIdentifier;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.DatastoreIdentifier.Enumeration;
/**
-**/
+ **/
public class DatastoreIdentifierBuilder {
- public static DatastoreIdentifier getDefaultInstance(String defaultValue) {
- throw new UnsupportedOperationException("Not yet implemented");
+ public static DatastoreIdentifier getDefaultInstance(final String defaultValue) {
+ return new DatastoreIdentifier(Enumeration.valueOf(defaultValue));
}
}
--- /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.yang.common;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * A general base exception for an operation failure.
+ *
+ * @author Thomas Pantelis
+ */
+public class OperationFailedException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ private final List<RpcError> errorList;
+
+ /**
+ * Constructs a new instance with the specified detail message and errors.
+ *
+ * @param message the detail message
+ * @param errors {@link RpcError} instances that provide additional error information about
+ * this exception
+ */
+ public OperationFailedException(final String message, final RpcError... errors) {
+ this(message, null, errors);
+ }
+
+ /**
+ * Constructs a new instance with the specified detail message, cause and errors.
+ *
+ * @param message the detail message
+ * @param cause the cause
+ * @param errors {@link RpcError} instances that provide additional error information about
+ * this exception
+ */
+ public OperationFailedException(final String message, final Throwable cause,
+ final RpcError... errors) {
+ super(Preconditions.checkNotNull(message), cause);
+
+ if( errors != null && errors.length > 0 ) {
+ errorList = ImmutableList.<RpcError>copyOf( Arrays.asList( errors ) );
+ }
+ else {
+ // Add a default RpcError.
+ errorList = ImmutableList.of(RpcResultBuilder.newError(ErrorType.APPLICATION, null,
+ getMessage(), null, null, getCause()));
+ }
+ }
+
+ /**
+ * Returns additional error information about this exception.
+ *
+ * @return a List of RpcErrors. There is always at least one RpcError.
+ */
+ public List<RpcError> getErrorList() {
+ return errorList;
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper( this ).add( "message", getMessage() )
+ .add( "errorList", errorList ).toString();
+ }
+}
return false;
}
final QNameModule other = (QNameModule) obj;
- if (namespace == null) {
- if (other.namespace != null) {
+ if (revision == null) {
+ if (other.revision != null) {
return false;
}
- } else if (!namespace.equals(other.namespace)) {
+ } else if (!revision.equals(other.revision)) {
return false;
}
- if (revision == null) {
- if (other.revision != null) {
+ if (namespace == null) {
+ if (other.namespace != null) {
return false;
}
- } else if (!revision.equals(other.revision)) {
+ } else if (!namespace.equals(other.namespace)) {
return false;
}
return true;
* @return an RpcError
*/
public static RpcError newError( ErrorType errorType, String tag, String message ) {
- return new RpcErrorImpl( ErrorSeverity.ERROR, errorType, tag, message, null, null, null );
+ return new RpcErrorImpl( ErrorSeverity.ERROR, errorType,
+ tag != null ? tag : "operation-failed", message, null, null, null );
}
/**
*/
public static RpcError newError( ErrorType errorType, String tag, String message,
String applicationTag, String info, Throwable cause ) {
- return new RpcErrorImpl( ErrorSeverity.ERROR, errorType, tag, message,
- applicationTag, info, cause );
+ return new RpcErrorImpl( ErrorSeverity.ERROR, errorType,
+ tag != null ? tag : "operation-failed", message, applicationTag, info, cause );
}
/**
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import java.io.Serializable;
import java.lang.reflect.Array;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.opendaylight.yangtools.concepts.Builder;
import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.util.HashCodeBuilder;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
/**
* Unique identifier of a partical node instance in the data tree.
*
- *
* <p>
* Java representation of YANG Built-in type <code>instance-identifier</code>,
* which conceptually is XPath expression minimised to uniquely identify element
*
*
* @see http://tools.ietf.org/html/rfc6020#section-9.13
- *
- *
*/
-public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable, Serializable {
+public final class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable, Serializable {
+ private static final InstanceIdentifier EMPTY = trustedCreate(Collections.<PathArgument>emptyList());
- private static final long serialVersionUID = 8467409862384206193L;
- private final ImmutableList<PathArgument> path;
+ private static final long serialVersionUID = 2L;
+ private final Iterable<PathArgument> pathArguments;
+ private final int hash;
+ private transient ImmutableList<PathArgument> legacyPath = null;
private transient String toStringCache = null;
- private transient Integer hashCodeCache = null;
+
+ private final ImmutableList<PathArgument> getLegacyPath() {
+ if (legacyPath == null) {
+ synchronized (this) {
+ if (legacyPath == null) {
+ legacyPath = ImmutableList.copyOf(pathArguments);
+ }
+ }
+ }
+
+ return legacyPath;
+ }
/**
- *
* Returns a list of path arguments.
*
* @deprecated Use {@link #getPathArguments()} instead.
*/
@Deprecated
public List<PathArgument> getPath() {
- return path;
+ return getLegacyPath();
}
/**
* @return Immutable iteration of path arguments.
*/
public Iterable<PathArgument> getPathArguments() {
- return path;
+ return pathArguments;
}
/**
* @return Immutable iterable of path arguments in reverse order.
*/
public Iterable<PathArgument> getReversePathArguments() {
- return path.reverse();
+ return getLegacyPath().reverse();
}
/**
* @return The last past argument, or null if there are no PathArguments.
*/
public PathArgument getLastPathArgument() {
- return Iterables.getFirst(path.reverse(), null);
+ return Iterables.getFirst(getReversePathArguments(), null);
}
- /**
- *
- *
- * @deprecated Use {@link #create(Iterable)} instead.
- * @param path
- */
- @Deprecated
- public InstanceIdentifier(final List<? extends PathArgument> path) {
- this.path = ImmutableList.copyOf(path);
+ private InstanceIdentifier(final Iterable<PathArgument> path, final int hash) {
+ this.pathArguments = Preconditions.checkNotNull(path, "path must not be null.");
+ this.hash = hash;
}
- private InstanceIdentifier(final Iterable<? extends PathArgument> path) {
- Preconditions.checkNotNull(path, "path must not be null.");
- this.path = ImmutableList.copyOf(path);
- }
+ private static final InstanceIdentifier trustedCreate(final Iterable<PathArgument> path) {
+ final HashCodeBuilder<PathArgument> hash = new HashCodeBuilder<>();
+ for (PathArgument a : path) {
+ hash.addArgument(a);
+ }
- private InstanceIdentifier(final NodeIdentifier nodeIdentifier) {
- this.path = ImmutableList.<PathArgument> of(nodeIdentifier);
+ return new InstanceIdentifier(path, hash.toInstance());
}
public static final InstanceIdentifier create(final Iterable<? extends PathArgument> path) {
- return new InstanceIdentifier(path);
+ if (Iterables.isEmpty(path)) {
+ return EMPTY;
+ }
+
+ return trustedCreate(ImmutableList.copyOf(path));
}
public static final InstanceIdentifier create(final PathArgument... path) {
+ // We are forcing a copy, since we cannot trust the user
return create(Arrays.asList(path));
}
@Override
public int hashCode() {
/*
- * The hashCodeCache is safe, since the object contract requires
+ * The caching is safe, since the object contract requires
* immutability of the object and all objects referenced from this
* object.
* Used lists, maps are immutable. Path Arguments (elements) are also
* immutable, since the PathArgument contract requires immutability.
- * The cache is thread-safe - if multiple computations occurs at the
- * same time, cache will be overwritten with same result.
*/
- if (hashCodeCache == null) {
- final int prime = 31;
- int result = 1;
- result = prime * result + path.hashCode();
- hashCodeCache = result;
- }
- return hashCodeCache;
+ return hash;
}
@Override
if (this.hashCode() != obj.hashCode()) {
return false;
}
- if (path == null) {
- if (other.path != null) {
- return false;
- }
- } else if (!path.equals(other.path)) {
- return false;
- }
- return true;
+ return Iterables.elementsEqual(pathArguments, other.pathArguments);
}
/**
- *
* Constructs a new Instance Identifier with new {@link NodeIdentifier} added to the end of path arguments
*
* @param name QName of {@link NodeIdentifier}
* @return Instance Identifier with additional path argument added to the end.
*/
public InstanceIdentifier node(final PathArgument arg) {
- return create(ImmutableList.<PathArgument> builder().addAll(path).add(arg).build());
+ return new InstanceIdentifier(Iterables.concat(pathArguments, Collections.singleton(arg)), HashCodeBuilder.nextHashCode(hash, arg));
}
/**
* the specified parent is not in fact an ancestor of this object.
*/
public Optional<InstanceIdentifier> relativeTo(final InstanceIdentifier ancestor) {
- if (ancestor.contains(this)) {
- final int common = ancestor.path.size();
- return Optional.of(new InstanceIdentifier(Iterables.skip(path, common)));
- } else {
- return Optional.absent();
+ final Iterator<?> lit = pathArguments.iterator();
+ final Iterator<?> oit = ancestor.pathArguments.iterator();
+ int common = 0;
+
+ while (oit.hasNext()) {
+ // Ancestor is not really an ancestor
+ if (!lit.hasNext() || !lit.next().equals(oit.next())) {
+ return Optional.absent();
+ }
+
+ ++common;
+ }
+
+ if (common == 0) {
+ return Optional.of(this);
+ }
+ if (!lit.hasNext()) {
+ return Optional.of(EMPTY);
}
+ return Optional.of(trustedCreate(Iterables.skip(pathArguments, common)));
}
- static int hashCode(final Object value) {
+ private static int hashCode(final Object value) {
if (value == null) {
return 0;
}
* @return Instance Identifier with only one path argument of type {@link NodeIdentifier}
*/
public static InstanceIdentifier of(final QName name) {
- return new InstanceIdentifier(new NodeIdentifier(name));
+ return create(new NodeIdentifier(name));
}
/**
* @return new builder for InstanceIdentifier with path arguments copied from original instance identifier.
*/
static public InstanceIdentifierBuilder builder(final InstanceIdentifier origin) {
- return new BuilderImpl(origin.getPath());
+ return new BuilderImpl(origin.getPathArguments(), origin.hashCode());
}
+
/**
- *
* Returns new builder for InstanceIdentifier with first path argument set to {@link NodeIdentifier}.
*
* @param node QName of first {@link NodeIdentifier} path argument.
* @return new builder for InstanceIdentifier with first path argument set to {@link NodeIdentifier}.
+ *
+ * @deprecated Either use {@link #node(QName)} or instantiate an intermediate builder.
*/
+ @Deprecated
public static InstanceIdentifierBuilder builder(final QName node) {
return builder().node(node);
}
/**
- *
* Path argument / component of InstanceIdentifier
*
* Path argument uniquelly identifies node in data tree on particular
* <li>{@link AugmentationIdentifier} - Identifier of augmentation
* <li>{@link NodeWithValue} - Identifier of leaf-list entry
* </ul>
- *
- *
- *
*/
public interface PathArgument extends Comparable<PathArgument>, Immutable, Serializable {
-
/**
* If applicable returns unique QName of data node as defined in YANG
* Schema.
* This method may return null, if the corresponding schema node, does
* not have QName associated, such as in cases of augmentations.
*
- * @return
+ * @return Node type
*/
QName getNodeType();
-
}
private static abstract class AbstractPathArgument implements PathArgument {
private static final long serialVersionUID = -4546547994250849340L;
- protected final QName nodeType;
+ private final QName nodeType;
protected AbstractPathArgument(final QName nodeType) {
this.nodeType = Preconditions.checkNotNull(nodeType);
}
@Override
- public QName getNodeType() {
+ public final QName getNodeType() {
return nodeType;
}
return nodeType.compareTo(o.getNodeType());
}
+ @Override
+ public int hashCode() {
+ return 31 + getNodeType().hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || this.getClass() != obj.getClass()) {
+ return false;
+ }
+
+ return getNodeType().equals(((AbstractPathArgument)obj).getNodeType());
+ }
+
+ @Override
+ public String toString() {
+ return getNodeType().toString();
+ }
}
/**
*
*/
public interface InstanceIdentifierBuilder extends Builder<InstanceIdentifier> {
-
/**
- *
* Adds {@link NodeIdentifier} with supplied QName to path arguments of resulting instance identifier.
*
* @param nodeType QName of {@link NodeIdentifier} which will be added
InstanceIdentifierBuilder node(QName nodeType);
/**
- *
* Adds {@link NodeIdentifierWithPredicates} with supplied QName and key values to path arguments of resulting instance identifier.
*
* @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map<QName, Object> keyValues);
/**
- *
* Adds {@link NodeIdentifierWithPredicates} with supplied QName and key, value.
*
* @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
*/
InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
- /**
- *
- * @return
- * @deprecated use {@link #build()}
- *
- */
- @Deprecated
- InstanceIdentifier getIdentifier();
-
/**
*
* Builds an {@link InstanceIdentifier} with path arguments from this builder
public NodeIdentifier(final QName node) {
super(node);
}
-
- @Override
- public int hashCode() {
- return 31 + nodeType.hashCode();
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof NodeIdentifier)) {
- return false;
- }
- final NodeIdentifier other = (NodeIdentifier) obj;
- return nodeType.equals(other.nodeType);
- }
-
- @Override
- public String toString() {
- return nodeType.toString();
- }
-
}
/**
this(node, ImmutableMap.of(key, value));
}
- @Override
- public QName getNodeType() {
- return nodeType;
- }
-
public Map<QName, Object> getKeyValues() {
return keyValues;
}
@Override
public int hashCode() {
final int prime = 31;
- int result = 1;
- result = prime * result + ((keyValues == null) ? 0 : hashKeyValues());
- result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
- return result;
- }
+ int result = super.hashCode();
+ result = prime * result;
- private int hashKeyValues() {
- int hash = 0;
for (Entry<QName, Object> entry : keyValues.entrySet()) {
- hash += Objects.hashCode(entry.getKey()) + InstanceIdentifier.hashCode(entry.getValue());
+ result += Objects.hashCode(entry.getKey()) + InstanceIdentifier.hashCode(entry.getValue());
}
-
- return hash;
+ return result;
}
@Override
public boolean equals(final Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
+ if (!super.equals(obj)) {
return false;
}
- if (getClass() != obj.getClass()) {
- return false;
- }
- NodeIdentifierWithPredicates other = (NodeIdentifierWithPredicates) obj;
- if (keyValues == null) {
- if (other.keyValues != null) {
- return false;
- }
- } else if (!keyValuesEquals(other.keyValues)) {
- return false;
- }
- if (nodeType == null) {
- if (other.nodeType != null) {
- return false;
- }
- } else if (!nodeType.equals(other.nodeType)) {
- return false;
- }
- return true;
- }
- private boolean keyValuesEquals(final Map<QName, Object> otherKeyValues) {
- if (otherKeyValues == null || keyValues.size() != otherKeyValues.size()) {
+ final Map<QName, Object> otherKeyValues = ((NodeIdentifierWithPredicates) obj).keyValues;
+ if (keyValues.size() != otherKeyValues.size()) {
return false;
}
- boolean result = true;
for (Entry<QName, Object> entry : keyValues.entrySet()) {
if (!otherKeyValues.containsKey(entry.getKey())
|| !Objects.deepEquals(entry.getValue(), otherKeyValues.get(entry.getKey()))) {
- result = false;
- break;
+ return false;
}
}
- return result;
+ return true;
}
@Override
public String toString() {
- return nodeType + "[" + keyValues + "]";
+ return super.toString() + '[' + keyValues + ']';
}
}
this.value = value;
}
- @Override
- public QName getNodeType() {
- return nodeType;
- }
-
public Object getValue() {
return value;
}
@Override
public int hashCode() {
final int prime = 31;
- int result = 1;
+ int result = super.hashCode();
result = prime * result + ((value == null) ? 0 : InstanceIdentifier.hashCode(value));
- result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
+ if (!super.equals(obj)) {
return false;
}
- if (getClass() != obj.getClass()) {
- return false;
- }
- NodeWithValue other = (NodeWithValue) obj;
- return Objects.deepEquals(value, other.value) && Objects.equals(nodeType, other.nodeType);
+ final NodeWithValue other = (NodeWithValue) obj;
+ return Objects.deepEquals(value, other.value);
}
@Override
public String toString() {
- return nodeType + "[" + value + "]";
+ return super.toString() + '[' + value + ']';
}
-
}
/**
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("AugmentationIdentifier{");
- sb.append("childNames=").append(childNames);
- sb.append('}');
+ sb.append("childNames=").append(childNames).append('}');
return sb.toString();
}
}
AugmentationIdentifier that = (AugmentationIdentifier) o;
-
- if (!childNames.equals(that.childNames)) {
- return false;
- }
-
- return true;
+ return childNames.equals(that.childNames);
}
@Override
}
private static class BuilderImpl implements InstanceIdentifierBuilder {
-
- private final ImmutableList.Builder<PathArgument> path;
+ private final HashCodeBuilder<PathArgument> hash;
+ private final List<PathArgument> path;
public BuilderImpl() {
- path = ImmutableList.<PathArgument> builder();
+ this.hash = new HashCodeBuilder<>();
+ this.path = new ArrayList<>();
}
- public BuilderImpl(final List<? extends PathArgument> prefix) {
- path = ImmutableList.<PathArgument> builder();
- path.addAll(prefix);
+ public BuilderImpl(final Iterable<PathArgument> prefix, final int hash) {
+ this.path = Lists.newArrayList(prefix);
+ this.hash = new HashCodeBuilder<>(hash);
}
@Override
public InstanceIdentifierBuilder node(final QName nodeType) {
- path.add(new NodeIdentifier(nodeType));
+ final PathArgument arg = new NodeIdentifier(nodeType);
+ path.add(arg);
+ hash.addArgument(arg);
return this;
}
@Override
public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final QName key, final Object value) {
- path.add(new NodeIdentifierWithPredicates(nodeType, key, value));
+ final PathArgument arg = new NodeIdentifierWithPredicates(nodeType, key, value);
+ path.add(arg);
+ hash.addArgument(arg);
return this;
}
@Override
public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final Map<QName, Object> keyValues) {
- path.add(new NodeIdentifierWithPredicates(nodeType, keyValues));
+ final PathArgument arg = new NodeIdentifierWithPredicates(nodeType, keyValues);
+ path.add(arg);
+ hash.addArgument(arg);
return this;
}
@Override
public InstanceIdentifier build() {
- return new InstanceIdentifier(path.build());
- }
-
- @Override
- @Deprecated
- public InstanceIdentifier getIdentifier() {
- return build();
+ return new InstanceIdentifier(ImmutableList.copyOf(path), hash.toInstance());
}
}
@Override
public boolean contains(final InstanceIdentifier other) {
- if (other == null) {
- throw new IllegalArgumentException("other should not be null");
- }
- final int localSize = this.path.size();
- final List<PathArgument> otherPath = other.getPath();
- if (localSize > other.path.size()) {
- return false;
- }
- for (int i = 0; i < localSize; i++) {
- if (!path.get(i).equals(otherPath.get(i))) {
+ Preconditions.checkArgument(other != null, "other should not be null");
+
+ final Iterator<?> lit = pathArguments.iterator();
+ final Iterator<?> oit = other.pathArguments.iterator();
+
+ while (lit.hasNext()) {
+ if (!oit.hasNext()) {
+ return false;
+ }
+
+ if (!lit.next().equals(oit.next())) {
return false;
}
}
+
return true;
}
final StringBuilder builder = new StringBuilder('/');
boolean first = true;
- for (PathArgument argument : path) {
+ for (PathArgument argument : getPathArguments()) {
if (first) {
first = false;
} else {
*/
package org.opendaylight.yangtools.yang.data.impl.schema.nodes;
+import com.google.common.base.Optional;
+
import java.util.Collections;
import java.util.Map;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
-import com.google.common.base.Optional;
-import com.google.common.collect.Iterables;
-
-public abstract class AbstractImmutableDataContainerNode<K extends PathArgument> //
- extends AbstractImmutableNormalizedNode<K, Iterable<DataContainerChild<? extends PathArgument, ?>>> //
- implements Immutable, DataContainerNode<K> {
+public abstract class AbstractImmutableDataContainerNode<K extends PathArgument> extends AbstractImmutableNormalizedNode<K, Iterable<DataContainerChild<? extends PathArgument, ?>>>
+implements Immutable, DataContainerNode<K> {
protected final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children;
+ private final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> publicChildren;
public AbstractImmutableDataContainerNode(
final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children, final K nodeIdentifier) {
super(nodeIdentifier);
this.children = children;
+ this.publicChildren = Collections.unmodifiableMap(children);
}
@Override
@Override
public final Iterable<DataContainerChild<? extends PathArgument, ?>> getValue() {
- return Iterables.unmodifiableIterable(children.values());
+ return publicChildren.values();
}
@Override
}
public final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> getChildren() {
- // Make sure we do not leak a mutable view
- return Collections.unmodifiableMap(children);
+ return publicChildren;
}
@Override
--- /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.api;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * A {@link SchemaSourceFilter} which accepts any schema source it is presented with.
+ */
+public final class AcceptingSchemaSourceFilter implements SchemaSourceFilter {
+ private static final AcceptingSchemaSourceFilter INSTANCE = new AcceptingSchemaSourceFilter();
+
+ private final Iterable<Class<? extends SchemaSourceRepresentation>> representations;
+
+ private AcceptingSchemaSourceFilter() {
+ final Builder<Class<? extends SchemaSourceRepresentation>> b = ImmutableList.builder();
+ b.add(SchemaSourceRepresentation.class);
+ representations = b.build();
+ }
+
+ /**
+ * Return the singleton instance of this filter.
+ *
+ * @return Singleton shared instance.
+ */
+ public static final AcceptingSchemaSourceFilter getSingletonInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public Iterable<Class<? extends SchemaSourceRepresentation>> supportedRepresentations() {
+ return representations;
+ }
+
+ @Override
+ public ListenableFuture<Boolean> apply(final SchemaSourceRepresentation schemaSource) {
+ return Futures.immediateFuture(Boolean.TRUE);
+ }
+}
--- /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.api;
+
+import com.google.common.util.concurrent.CheckedFuture;
+
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * An asynchronous factory for building {@link SchemaContext} instances
+ * based on a specification of what {@link SourceIdentifier}s are required
+ * and dynamic recursive resolution.
+ */
+public interface SchemaContextFactory {
+ /**
+ * Create a new schema context containing specified sources, pulling in
+ * any dependencies they may have.
+ *
+ * @param requiredSources a collection of sources which are required to
+ * be present
+ * @return A checked future, which will produce a schema context, or
+ * fail with an explanation why the creation of the schema context
+ * failed.
+ */
+ CheckedFuture<SchemaContext, SchemaResolutionException> createSchemaContext(@Nonnull Collection<SourceIdentifier> requiredSources);
+}
--- /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.api;
+
+import javax.annotation.Nonnull;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Interface exposed by repository implementations. A schema repository is a logically
+ * centralized place for model storage and creation of {@link SchemaContext} instances.
+ */
+public interface SchemaRepository {
+ /**
+ * Instantiate a new {@link SchemaContextFactory}, which will filter available schema
+ * sources using the provided filter.
+ *
+ * @param filter Filter which acts as the gating function before a schema source is
+ * considered by the factory for inclusion in the {@link SchemaContext}
+ * it produces.
+ * @return A new schema context factory.
+ */
+ SchemaContextFactory createSchemaContextFactory(@Nonnull SchemaSourceFilter filter);
+}
--- /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.api;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Exception thrown when a Schema Source fails to resolve.
+ */
+public class SchemaResolutionException extends Exception {
+ private static final long serialVersionUID = 1L;
+ private final Map<SourceIdentifier, Throwable> unresolvedSources;
+
+ public SchemaResolutionException(final @Nonnull String message) {
+ this(message, (Throwable)null);
+ }
+
+ public SchemaResolutionException(final @Nonnull String message, final Throwable cause) {
+ this(message, cause, ImmutableMap.<SourceIdentifier, Exception>of());
+ }
+
+ public SchemaResolutionException(final @Nonnull String message, final @Nonnull Map<SourceIdentifier, ? extends Throwable> unresolvedSources) {
+ super(Preconditions.checkNotNull(message));
+ this.unresolvedSources = ImmutableMap.copyOf(unresolvedSources);
+ }
+
+ public SchemaResolutionException(final @Nonnull String message, final Throwable cause, @Nonnull final Map<SourceIdentifier, ? extends Throwable> unresolvedSources) {
+ super(message, cause);
+ this.unresolvedSources = ImmutableMap.copyOf(unresolvedSources);
+ }
+
+ /**
+ * Return the list of sources which failed to resolve along with reasons
+ * why they were not resolved.
+ *
+ * @return Source/reason map.
+ */
+ public final Map<SourceIdentifier, Throwable> getUnresolvedSources() {
+ return unresolvedSources;
+ }
+
+ @Override
+ public final String toString() {
+ return addToStringAttributes(Objects.toStringHelper(this).add("unresolvedSources", unresolvedSources)).toString();
+ }
+
+ protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+ return toStringHelper;
+ }
+}
--- /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.api;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface SchemaSourceFilter {
+ Iterable<Class<? extends SchemaSourceRepresentation>> supportedRepresentations();
+ ListenableFuture<Boolean> apply(SchemaSourceRepresentation schemaSource);
+}
--- /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.api;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * Common interface for schema source representations.
+ *
+ * A schema source is an atomic piece of the overall schema context. In YANG terms,
+ * a schema source is semantically equivalent to a single YANG text file, be it a
+ * module or a submodule.
+ *
+ * A schema source can exist in various forms, which we call representations. Again,
+ * in YANG terms, each representation is semantically equivalent, but from
+ * implementation perspective certain operations on a schema source may require it
+ * to be first transformed into a particular representation before they can be
+ * applied. Such transformations are affected via instances of
+ * {@link SchemaSourceTransformation}.
+ *
+ * Typical examples of a schema source representation include:
+ * <ul>
+ * <li>a {@link java.lang.String} - textual representation of source code
+ * <li>a {@link java.io.InputStream} - input stream containing source code
+ * <li>a {@link com.google.common.io.ByteSource} - source for input streams
+ * containing source code
+ * <li>Parsed abstract syntax tree (AST), which is the result of a syntactic parser
+ *
+ * Implementations of this interface expected to comply with the {@link Immutable}
+ * contract.
+ */
+public interface SchemaSourceRepresentation extends Identifiable<SourceIdentifier>, Immutable {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ SourceIdentifier getIdentifier();
+
+ /**
+ * Return the concrete representation type.
+ *
+ * @return The type of representation.
+ */
+ Class<? extends SchemaSourceRepresentation> getType();
+}
--- /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.api;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.concepts.Identifier;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ *
+ * YANG Schema source identifier
+ *
+ * Simple transfer object represents identifier of source for YANG schema (module or submodule),
+ * which consists of
+ * <ul>
+ * <li>YANG schema name ({@link #getName()}
+ * <li>Module revision (optional) ({link {@link #getRevision()})
+ * </ul>
+ *
+ * Source identifier is designated to be carry only necessary information
+ * to look-up YANG model source and to be used by {@link AdvancedSchemaSourceProvider}
+ * and similar.
+ *
+ * <b>Note:</b>On source retrieval layer it is impossible to distinguish
+ * between YANG module and/or submodule unless source is present.
+ *
+ * <p>
+ * (For further reference see: http://tools.ietf.org/html/rfc6020#section-5.2 and
+ * http://tools.ietf.org/html/rfc6022#section-3.1 ).
+ *
+ *
+ */
+public final class SourceIdentifier implements Identifier, Immutable {
+ private static final long serialVersionUID = 1L;
+ private final String revision;
+ private final String name;
+
+ /**
+ *
+ * Creates new YANG Schema source identifier.
+ *
+ * @param name Name of schema
+ * @param formattedRevision Revision of source in format YYYY-mm-dd
+ */
+ public SourceIdentifier(final String name, final Optional<String> formattedRevision) {
+ super();
+ this.name = Preconditions.checkNotNull(name);
+ this.revision = formattedRevision.orNull();
+ }
+
+ /**
+ * Returns model name
+ *
+ * @return model name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns revision of source or null if revision was not supplied.
+ *
+ * @return revision of source or null if revision was not supplied.
+ */
+ public String getRevision() {
+ return revision;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((revision == null) ? 0 : revision.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ SourceIdentifier other = (SourceIdentifier) obj;
+ if (name == null) {
+ if (other.name != null) {
+ return false;
+ }
+ } else if (!name.equals(other.name)) {
+ return false;
+ }
+ if (revision == null) {
+ if (other.revision != null) {
+ return false;
+ }
+ } else if (!revision.equals(other.revision)) {
+ return false;
+ }
+ return true;
+ }
+
+ public static SourceIdentifier create(final String moduleName, final Optional<String> revision) {
+ return new SourceIdentifier(moduleName, revision);
+ }
+
+ /**
+ * Returns filename for this YANG module as specified in RFC 6020.
+ *
+ * Returns filename in format
+ * <code>name ['@' revision] '.yang'</code>
+ * <p>
+ * Where revision is date in format YYYY-mm-dd.
+ * <p>
+ *
+ * @see http://tools.ietf.org/html/rfc6020#section-5.2
+ *
+ * @return Filename for this source identifier.
+ */
+ public String toYangFilename() {
+ return toYangFileName(name, Optional.fromNullable(revision));
+ }
+
+ @Override
+ public String toString() {
+ return "SourceIdentifier [name=" + name + "@" + revision + "]";
+ }
+
+ /**
+ * Returns filename for this YANG module as specified in RFC 6020.
+ *
+ * Returns filename in format
+ * <code>moduleName ['@' revision] '.yang'</code>
+ *
+ * Where Where revision-date is in format YYYY-mm-dd.
+ *
+ * <p>
+ * See
+ * http://tools.ietf.org/html/rfc6020#section-5.2
+ *
+ * @return Filename for this source identifier.
+ */
+ public static final String toYangFileName(final String moduleName, final Optional<String> revision) {
+ StringBuilder filename = new StringBuilder(moduleName);
+ if (revision.isPresent()) {
+ filename.append('@');
+ filename.append(revision.get());
+ }
+ filename.append(".yang");
+ return filename.toString();
+ }
+
+}
--- /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.api;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.io.ByteSource;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.opendaylight.yangtools.concepts.Delegator;
+
+/**
+ * YANG text schema source representation. Exposes an RFC6020 text representation
+ * as an {@link InputStream}.
+ */
+public abstract class YangTextSchemaSource extends ByteSource implements SchemaSourceRepresentation {
+ private final SourceIdentifier identifier;
+
+ protected YangTextSchemaSource(final SourceIdentifier identifier) {
+ this.identifier = Preconditions.checkNotNull(identifier);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final SourceIdentifier getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Class<? extends YangTextSchemaSource> getType() {
+ return YangTextSchemaSource.class;
+ }
+
+ @Override
+ public final String toString() {
+ return addToStringAttributes(Objects.toStringHelper(this).add("identifier", identifier)).toString();
+ }
+
+ /**
+ * Add subclass-specific attributes to the output {@link #toString()} output. Since
+ * subclasses are prevented from overriding {@link #toString()} for consistency
+ * reasons, they can add their specific attributes to the resulting string by attaching
+ * attributes to the supplied {@link ToStringHelper}.
+ *
+ * @param toStringHelper ToStringHelper onto the attributes can be added
+ * @return ToStringHelper supplied as input argument.
+ */
+ protected abstract ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper);
+
+ /**
+ * Create a new YangTextSchemaSource with a specific source identifier and backed
+ * by ByteSource, which provides the actual InputStreams.
+ *
+ * @param identifier SourceIdentifier of the resulting schema source
+ * @param delegate Backing ByteSource instance
+ * @return A new YangTextSchemaSource
+ */
+ public static YangTextSchemaSource delegateForByteSource(final SourceIdentifier identifier, final ByteSource delegate) {
+ return new DelegatedYangTextSchemaSource(identifier, delegate);
+ }
+
+ private static final class DelegatedYangTextSchemaSource extends YangTextSchemaSource implements Delegator<ByteSource> {
+ private final ByteSource delegate;
+
+ private DelegatedYangTextSchemaSource(final SourceIdentifier identifier, final ByteSource delegate) {
+ super(identifier);
+ this.delegate = Preconditions.checkNotNull(delegate);
+ }
+
+ @Override
+ public final ByteSource getDelegate() {
+ return delegate;
+ }
+
+ @Override
+ public InputStream openStream() throws IOException {
+ return delegate.openStream();
+ }
+
+ @Override
+ protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+ return toStringHelper.add("delegate", delegate);
+ }
+ }
+}
--- /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.api;
+
+import org.w3c.dom.Document;
+
+/**
+ * Yin schema source representation. Exposes an RFC6020 YIN XML representation
+ * as an W3C {@link Document}.
+ */
+public interface YinSchemaSource extends SchemaSourceRepresentation {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ SourceIdentifier getIdentifier();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ Class<? extends YinSchemaSource> getType();
+
+ /**
+ * Return schema source as a Yin-compliant Document.
+ *
+ * @return W3C DOM document.
+ */
+ Document getYinDocument();
+}
--- /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.spi;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Schema source provider implementations take care of resolving a {@link SourceIdentifier}
+ * into a particular representation of the schema source. Examples of resolution include
+ * fetching the source from an external source, opening a classpath resource, or similar.
+ *
+ * @param <T> Schema source representation type provided by this implementation
+ */
+@Beta
+public interface SchemaSourceProvider<T extends SchemaSourceRepresentation> {
+ /**
+ * Returns a representation a for supplied YANG source identifier. The resolution
+ * criteria are as follows:
+ *
+ * <ul>
+ * <li> If the source identifier specifies a revision, this method returns either
+ * a representation of that particular revision, or report the identifier as absent
+ * by returning {@link Optional#absent()}.
+ * <li> If the source identifier does not specify a revision, this method returns
+ * the newest available revision, or {@link Optional#absent()}.
+ *
+ * In either case the returned representation is required to report a non-null
+ * revision in the {@link SourceIdentifier} returned from
+ * {@link SchemaSourceRepresentation#getIdentifier()}.
+ *
+ * Implementations are not required to provide constant behavior in time, notably
+ * this different invocation of this method may produce different results.
+ *
+ * @param sourceIdentifier source identifier
+ * @return source representation if supplied YANG module is available
+ * {@link Optional#absent()} otherwise.
+ */
+ ListenableFuture<Optional<T>> getSource(SourceIdentifier sourceIdentifier);
+}
--- /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.spi;
+
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+public interface SchemaSourceRegistration extends Registration<SourceIdentifier> {
+ @Override
+ void close();
+}
--- /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.spi;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Registry of all potentially available schema sources. Processes capable of
+ * dynamic schema discovery, such as OSGi registry scanners, NETCONF clients
+ * (with NETCONF monitoring extension) and similar can register
+ * {@link SchemaSourceProvider} instances which would then acquire the schema
+ * source.
+ */
+public interface SchemaSourceRegistry {
+ /**
+ * Register a new schema source which is potentially available from a provider.
+ * A registration does not guarantee that a subsequent call to
+ * {@link SchemaSourceProvider#getSource(SourceIdentifier)} will succeed.
+ *
+ * @param identifier Schema source identifier
+ * @param provider Resolver which can potentially resolve the identifier
+ * @param representation Schema source representation which the source may
+ * be available.
+ * @return A registration handle. Invoking {@link SchemaSourceRegistration#close()}
+ * will cancel the registration.
+ */
+ <T extends SchemaSourceRepresentation> SchemaSourceRegistration registerSchemaSource(
+ SourceIdentifier identifier, SchemaSourceProvider<? super T> provider, Class<T> representation);
+
+ /**
+ * Register a schema transformer. The registry can invoke it to transform between
+ * the various schema source formats.
+ *
+ * @param transformer Schema source transformer
+ * @return A registration handle. Invoking {@link SchemaTransformerRegistration#close()}
+ * will cancel the registration.
+ */
+ SchemaTransformerRegistration registerSchemaSourceTransformer(SchemaSourceTransformer<?, ?> transformer);
+}
--- /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.spi;
+
+/**
+ * Exception thrown when a failure to translate a schema source between
+ * representations.
+ */
+public class SchemaSourceTransformationException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public SchemaSourceTransformationException(final String message) {
+ super(message);
+ }
+
+ public SchemaSourceTransformationException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
--- /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.spi;
+
+import com.google.common.util.concurrent.CheckedFuture;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+
+/**
+ * An schema source representation transformation service. An instance can create
+ * some output schema source representation based on some input source representation.
+ *
+ * @param <I> Input {@link SchemaSourceRepresentation}
+ * @param <O> Output {@link SchemaSourceRepresentation}
+ */
+public interface SchemaSourceTransformer<I extends SchemaSourceRepresentation, O extends SchemaSourceRepresentation> {
+ /**
+ * Return the {@link SchemaSourceRepresentation} which this transformer
+ * accepts on its input.
+ *
+ * @return The input source representation type.
+ */
+ Class<I> getInputRepresentation();
+
+ /**
+ * Return the {@link SchemeSourceRepresentation} which this transformer
+ * produces on its output.
+ *
+ * @return The output source representation type.
+ */
+ Class<O> getOutputRepresentation();
+
+ /**
+ * Transform a schema source representation from its input form to
+ * the transformers output form.
+ *
+ * @param source Schema source in its source representation
+ * @return A future which produces the output schema source representation.
+ */
+ CheckedFuture<O, SchemaSourceTransformationException> transformSchemaSource(I source);
+
+ /**
+ * Return the relative cost of performing the transformation. When in doubt,
+ * return 1.
+ *
+ * @return Relative cost.
+ */
+ int getCost();
+}
--- /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.spi;
+
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+
+public interface SchemaTransformerRegistration extends ObjectRegistration<SchemaSourceTransformer<?, ?>> {
+ @Override
+ void close();
+}
+/*
+ * 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.builder.util;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+
import java.util.Map;
import java.util.Set;
+
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
@Override
public final DataSchemaNode getDataChildByName(final QName name) {
- for (DataSchemaNode node : childNodes.values()) {
- if (node.getQName().equals(name)) {
- return node;
- }
- }
- return null;
+ // Child nodes are keyed by their container name, so we can do a direct lookup
+ return childNodes.get(name);
}
@Override