X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-model-util%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fmodel%2Frepo%2Futil%2FAbstractSchemaRepository.java;h=41b637482ac069bb40ac2857267b0491e4e1421f;hb=d559b39d9c161ab05f7304f522eae0ce31fdb67a;hp=4dd30b90a99c3470c92cbd1082ed2793598d4bd1;hpb=0b5bd33d5b1bc99df1c787026c87c138ade6f4ae;p=yangtools.git diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java index 4dd30b90a9..41b637482a 100644 --- a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java @@ -3,189 +3,211 @@ * * 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.base.Optional; -import com.google.common.collect.HashMultimap; +import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFailedFluentFuture; + +import com.google.common.annotations.Beta; +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.AsyncFunction; +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.MoreExecutors; 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 javax.annotation.Nonnull; +import javax.annotation.concurrent.GuardedBy; +import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException; 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.PotentialSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaListenerRegistration; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener; 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 { +/** + * Abstract base class for {@link SchemaRepository} implementations. It handles registration + * and lookup of schema sources, subclasses need only to provide their own + * {@link #createSchemaContextFactory(SchemaSourceFilter)} implementation. + */ +@Beta +public abstract class AbstractSchemaRepository implements SchemaRepository, SchemaSourceRegistry { private static final Logger LOG = LoggerFactory.getLogger(AbstractSchemaRepository.class); - private static final Comparator TRANSFORMER_COST_COMPARATOR = new Comparator() { - @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. + * Source identifier -> representation -> provider map. We usually are looking for + * a specific representation of a source. */ - private final Multimap, SchemaTransformerRegistration> transformers = - HashMultimap.create(); + @GuardedBy("this") + private final Map, + AbstractSchemaSourceRegistration>> sources = new HashMap<>(); /* - * Source identifier -> representation -> provider map. We usually are looking for - * a specific representation a source. + * Schema source listeners. */ - private final Map, AbstractSchemaSourceRegistration>> sources = new HashMap<>(); + @GuardedBy("this") + private final Collection listeners = new ArrayList<>(); + @SuppressWarnings("unchecked") + private static ListenableFuture fetchSource( + final SourceIdentifier id, final Iterator> it) { + final AbstractSchemaSourceRegistration reg = it.next(); - private static final ListenableFuture> fetchSource(final SourceIdentifier id, final Iterator it) { - if (!it.hasNext()) { - return Futures.immediateFuture(Optional.absent()); - } + return Futures.catchingAsync(((SchemaSourceProvider)reg.getProvider()).getSource(id), Throwable.class, + input -> { + LOG.debug("Failed to acquire source from {}", reg, input); - return Futures.transform(((SchemaSourceProvider)it.next().getProvider()).getSource(id), new AsyncFunction, Optional>() { - @Override - public ListenableFuture> apply(final Optional input) throws Exception { - if (input.isPresent()) { - return Futures.immediateFuture(input); - } else { + if (it.hasNext()) { return fetchSource(id, it); } - } - }); - } - private ListenableFuture> transformSchemaSource(final SourceIdentifier id, final Class representation) { - final Multimap, 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 ts = transformers.get(representation); - if (ts.isEmpty()) { - return Futures.immediateFailedFuture(new SchemaSourceTransformationException( - String.format("No transformers producing representation %s registered", representation))); - } + throw new MissingSchemaSourceException("All available providers exhausted", id, input); + }, MoreExecutors.directExecutor()); + } - // Build up the candidate list - final List 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); + @Override + public ListenableFuture getSchemaSource( + @Nonnull final SourceIdentifier id, @Nonnull final Class representation) { + final ArrayList> sortedSchemaSourceRegistrations; + + synchronized (this) { + final ListMultimap, AbstractSchemaSourceRegistration> srcs = + sources.get(id); + if (srcs == null) { + return immediateFailedFluentFuture(new MissingSchemaSourceException( + "No providers registered for source" + id, id)); } - } - if (candidates.isEmpty()) { - return Futures.immediateFailedFuture(new SchemaSourceTransformationException( - String.format("No matching source/transformer pair for source %s representation %s found", id, representation))); + sortedSchemaSourceRegistrations = Lists.newArrayList(srcs.get(representation)); } - Collections.sort(candidates, TRANSFORMER_COST_COMPARATOR); - // return transform(candidates.iterator(), id); - return null; - } + // TODO, remove and make sources keep sorted multimap (e.g. ArrayListMultimap with SortedLists) + sortedSchemaSourceRegistrations.sort(SchemaProviderCostComparator.INSTANCE); - /** - * Obtain a SchemaSource is selected representation - */ - protected ListenableFuture> getSchemaSource(final SourceIdentifier id, final Class representation) { - final Multimap, AbstractSchemaSourceRegistration> srcs = sources.get(id); - if (srcs == null) { - LOG.debug("No providers registered for source {}", id); - return Futures.immediateFuture(Optional.absent()); + final Iterator> regs = sortedSchemaSourceRegistrations.iterator(); + if (!regs.hasNext()) { + return immediateFailedFluentFuture(new MissingSchemaSourceException( + "No providers for source " + id + " representation " + representation + " available", id)); } - final Collection candidates = srcs.get(representation); - return Futures.transform(AbstractSchemaRepository.fetchSource(id, candidates.iterator()), new AsyncFunction, Optional>() { + final ListenableFuture fetchSourceFuture = fetchSource(id, regs); + // Add callback to notify cache listeners about encountered schema + Futures.addCallback(fetchSourceFuture, new FutureCallback() { @Override - public ListenableFuture> apply(final Optional input) throws Exception { - if (input.isPresent()) { - return Futures.immediateFuture(input); + public void onSuccess(final T result) { + for (final SchemaListenerRegistration listener : listeners) { + listener.getInstance().schemaSourceEncountered(result); } + } - return transformSchemaSource(id, representation); + @Override + @SuppressWarnings("checkstyle:parameterName") + public void onFailure(@Nonnull final Throwable t) { + LOG.trace("Skipping notification for encountered source {}, fetching source failed", id, t); } - }); - } + }, MoreExecutors.directExecutor()); - @Override - public SchemaContextFactory createSchemaContextFactory(final SchemaSourceFilter filter) { - // TODO Auto-generated method stub - return null; + return fetchSourceFuture; } - private void addSource(final SourceIdentifier id, final Class rep, final AbstractSchemaSourceRegistration reg) { - Multimap, AbstractSchemaSourceRegistration> m = sources.get(id); - if (m == null) { - m = HashMultimap.create(); - sources.put(id, m); + private synchronized void addSource(final PotentialSchemaSource source, + final AbstractSchemaSourceRegistration reg) { + ListMultimap, AbstractSchemaSourceRegistration> map = + sources.get(source.getSourceIdentifier()); + if (map == null) { + map = ArrayListMultimap.create(); + sources.put(source.getSourceIdentifier(), map); } - m.put(rep, reg); + map.put(source.getRepresentation(), reg); + + final Collection> reps = Collections.singleton(source); + for (SchemaListenerRegistration l : listeners) { + l.getInstance().schemaSourceRegistered(reps); + } } - private void removeSource(final SourceIdentifier id, final Class rep, final SchemaSourceRegistration reg) { - final Multimap, AbstractSchemaSourceRegistration> m = sources.get(id); + private synchronized void removeSource(final PotentialSchemaSource source, + final SchemaSourceRegistration reg) { + final Multimap, AbstractSchemaSourceRegistration> m = + sources.get(source.getSourceIdentifier()); if (m != null) { - m.remove(rep, reg); + m.remove(source.getRepresentation(), reg); + + for (SchemaListenerRegistration l : listeners) { + l.getInstance().schemaSourceUnregistered(source); + } + if (m.isEmpty()) { - sources.remove(m); + sources.remove(source.getSourceIdentifier()); } } } @Override - public SchemaSourceRegistration registerSchemaSource( - final SourceIdentifier identifier, final SchemaSourceProvider provider, final Class representation) { - final AbstractSchemaSourceRegistration ret = new AbstractSchemaSourceRegistration(identifier, provider) { + public SchemaSourceRegistration registerSchemaSource( + final SchemaSourceProvider provider, final PotentialSchemaSource source) { + final PotentialSchemaSource src = source.cachedReference(); + + final AbstractSchemaSourceRegistration ret = new AbstractSchemaSourceRegistration(provider, src) { @Override protected void removeRegistration() { - removeSource(identifier, representation, this); + removeSource(src, this); } }; - addSource(identifier, representation, ret); + addSource(src, ret); return ret; } @Override - public SchemaTransformerRegistration registerSchemaSourceTransformer(final SchemaSourceTransformer transformer) { - final SchemaTransformerRegistration ret = new AbstractSchemaTransformerRegistration(transformer) { + public SchemaListenerRegistration registerSchemaSourceListener(final SchemaSourceListener listener) { + final SchemaListenerRegistration ret = new AbstractSchemaListenerRegistration(listener) { @Override protected void removeRegistration() { - transformers.remove(transformer.getOutputRepresentation(), this); + listeners.remove(this); } }; - transformers.put(transformer.getOutputRepresentation(), ret); + synchronized (this) { + final Collection> col = new ArrayList<>(); + for (Multimap, AbstractSchemaSourceRegistration> m + : sources.values()) { + for (AbstractSchemaSourceRegistration r : m.values()) { + col.add(r.getInstance()); + } + } + + // Notify first, so translator-type listeners, who react by registering a source + // do not cause infinite loop. + listener.schemaSourceRegistered(col); + listeners.add(ret); + } return ret; } + + private static class SchemaProviderCostComparator implements Comparator> { + public static final SchemaProviderCostComparator INSTANCE = new SchemaProviderCostComparator(); + + @Override + public int compare(final AbstractSchemaSourceRegistration o1, final AbstractSchemaSourceRegistration o2) { + return o1.getInstance().getCost() - o2.getInstance().getCost(); + } + } }