Bug 5819 - Violation of synchronization rules in AbstractSchemaRepository
[yangtools.git] / yang / yang-model-util / src / main / java / org / opendaylight / yangtools / yang / model / repo / util / AbstractSchemaRepository.java
index 53563fde69cea887b7d0a90e5be70350f58e8925..2413c6fc9679891a49d3a80ab25119b7aadf619c 100644 (file)
@@ -3,27 +3,28 @@
  *
  * 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
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 package org.opendaylight.yangtools.yang.model.repo.util;
 
 import com.google.common.annotations.Beta;
-import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.FutureFallback;
 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.Map;
-
 import javax.annotation.concurrent.GuardedBy;
-
 import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
 import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper;
 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
@@ -56,7 +57,7 @@ public abstract class AbstractSchemaRepository implements SchemaRepository, Sche
      * a specific representation of a source.
      */
     @GuardedBy("this")
-    private final Map<SourceIdentifier, Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>>> sources = new HashMap<>();
+    private final Map<SourceIdentifier, ListMultimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>>> sources = new HashMap<>();
 
     /*
      * Schema source listeners.
@@ -64,11 +65,12 @@ public abstract class AbstractSchemaRepository implements SchemaRepository, Sche
     @GuardedBy("this")
     private final Collection<SchemaListenerRegistration> listeners = new ArrayList<>();
 
-    private static final <T extends SchemaSourceRepresentation> CheckedFuture<T, SchemaSourceException> fetchSource(final SourceIdentifier id, final Iterator<AbstractSchemaSourceRegistration<?>> it) {
+    private static <T extends SchemaSourceRepresentation> CheckedFuture<T, SchemaSourceException> fetchSource(final SourceIdentifier id, final Iterator<AbstractSchemaSourceRegistration<?>> it) {
         final AbstractSchemaSourceRegistration<?> reg = it.next();
 
         @SuppressWarnings("unchecked")
         final CheckedFuture<? extends T, SchemaSourceException> f = ((SchemaSourceProvider<T>)reg.getProvider()).getSource(id);
+
         return Futures.makeChecked(Futures.withFallback(f, new FutureFallback<T>() {
             @Override
             public ListenableFuture<T> create(final Throwable t) throws SchemaSourceException {
@@ -78,31 +80,56 @@ public abstract class AbstractSchemaRepository implements SchemaRepository, Sche
                     return fetchSource(id, it);
                 }
 
-                throw new MissingSchemaSourceException("All available providers exhausted");
+                throw new MissingSchemaSourceException("All available providers exhausted", id, t);
             }
         }), FETCH_MAPPER);
     }
 
     @Override
     public <T extends SchemaSourceRepresentation> CheckedFuture<T, SchemaSourceException> getSchemaSource(final SourceIdentifier id, final Class<T> representation) {
-        final Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> srcs = sources.get(id);
-        if (srcs == null) {
-            return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(new MissingSchemaSourceException("No providers registered for source" + id));
+        final ArrayList<AbstractSchemaSourceRegistration<?>> sortedSchemaSourceRegistrations;
+
+        synchronized (this) {
+            final ListMultimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> srcs = sources.get(id);
+            if (srcs == null) {
+                return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(new MissingSchemaSourceException("No providers registered for source" + id, id));
+            }
+
+            sortedSchemaSourceRegistrations = Lists.newArrayList(srcs.get(representation));
         }
 
-        final Iterator<AbstractSchemaSourceRegistration<?>> regs = srcs.get(representation).iterator();
+        // TODO, remove and make sources keep sorted multimap (e.g. ArrayListMultimap with SortedLists)
+        Collections.sort(sortedSchemaSourceRegistrations, SchemaProviderCostComparator.INSTANCE);
+
+        final Iterator<AbstractSchemaSourceRegistration<?>> regs = sortedSchemaSourceRegistrations.iterator();
         if (!regs.hasNext()) {
             return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(
-                    new MissingSchemaSourceException("No providers for source " + id + " representation " + representation + " available"));
+                    new MissingSchemaSourceException("No providers for source " + id + " representation " + representation + " available", id));
         }
 
-        return fetchSource(id, regs);
+        CheckedFuture<T, SchemaSourceException> fetchSourceFuture = fetchSource(id, regs);
+        // Add callback to notify cache listeners about encountered schema
+        Futures.addCallback(fetchSourceFuture, new FutureCallback<T>() {
+            @Override
+            public void onSuccess(final T result) {
+                for (final SchemaListenerRegistration listener : listeners) {
+                    listener.getInstance().schemaSourceEncountered(result);
+                }
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                LOG.trace("Skipping notification for encountered source {}, fetching source failed", id, t);
+            }
+        });
+
+        return fetchSourceFuture;
     }
 
     private synchronized <T extends SchemaSourceRepresentation> void addSource(final PotentialSchemaSource<T> source, final AbstractSchemaSourceRegistration<T> reg) {
-        Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> m = sources.get(source.getSourceIdentifier());
+        ListMultimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> m = sources.get(source.getSourceIdentifier());
         if (m == null) {
-            m = HashMultimap.create();
+            m = ArrayListMultimap.create();
             sources.put(source.getSourceIdentifier(), m);
         }
 
@@ -124,21 +151,23 @@ public abstract class AbstractSchemaRepository implements SchemaRepository, Sche
             }
 
             if (m.isEmpty()) {
-                sources.remove(m);
+                sources.remove(source.getSourceIdentifier());
             }
         }
     }
 
     @Override
     public <T extends SchemaSourceRepresentation> SchemaSourceRegistration<T> registerSchemaSource(final SchemaSourceProvider<? super T> provider, final PotentialSchemaSource<T> source) {
-        final AbstractSchemaSourceRegistration<T> ret = new AbstractSchemaSourceRegistration<T>(provider, source) {
+        final PotentialSchemaSource<T> src = source.cachedReference();
+
+        final AbstractSchemaSourceRegistration<T> ret = new AbstractSchemaSourceRegistration<T>(provider, src) {
             @Override
             protected void removeRegistration() {
-                removeSource(source, this);
+                removeSource(src, this);
             }
         };
 
-        addSource(source, ret);
+        addSource(src, ret);
         return ret;
     }
 
@@ -166,4 +195,13 @@ public abstract class AbstractSchemaRepository implements SchemaRepository, Sche
         }
         return ret;
     }
+
+    private static class SchemaProviderCostComparator implements Comparator<AbstractSchemaSourceRegistration<?>> {
+        public static final SchemaProviderCostComparator INSTANCE = new SchemaProviderCostComparator();
+
+        @Override
+        public int compare(final AbstractSchemaSourceRegistration<?> o1, final AbstractSchemaSourceRegistration<?> o2) {
+            return o1.getInstance().getCost() - o2.getInstance().getCost();
+        }
+    }
 }