*
* 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 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.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.List;
import java.util.Map;
-
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+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;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
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<SchemaTransformerRegistration> TRANSFORMER_COST_COMPARATOR = new Comparator<SchemaTransformerRegistration>() {
- @Override
- public int compare(final SchemaTransformerRegistration o1, final SchemaTransformerRegistration o2) {
- return o1.getInstance().getCost() - o2.getInstance().getCost();
- }
- };
+ private static final ExceptionMapper<SchemaSourceException> FETCH_MAPPER = ReflectiveExceptionMapper.create("Schema source fetch", SchemaSourceException.class);
/*
- * 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<Class<? extends SchemaSourceRepresentation>, SchemaTransformerRegistration> transformers =
- HashMultimap.create();
+ @GuardedBy("this")
+ private final Map<SourceIdentifier, ListMultimap<Class<? extends SchemaSourceRepresentation>, 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<SourceIdentifier, Multimap<Class<?>, AbstractSchemaSourceRegistration>> sources = new HashMap<>();
+ @GuardedBy("this")
+ private final Collection<SchemaListenerRegistration> listeners = new ArrayList<>();
+ private static <T extends SchemaSourceRepresentation> CheckedFuture<T, SchemaSourceException> fetchSource(final SourceIdentifier id, final Iterator<AbstractSchemaSourceRegistration<?>> it) {
+ final AbstractSchemaSourceRegistration<?> reg = it.next();
- private static final <T extends SchemaSourceRepresentation> ListenableFuture<Optional<T>> fetchSource(final SourceIdentifier id, final Iterator<AbstractSchemaSourceRegistration> it) {
- if (!it.hasNext()) {
- return Futures.immediateFuture(Optional.<T>absent());
- }
+ @SuppressWarnings("unchecked")
+ final CheckedFuture<? extends T, SchemaSourceException> f = ((SchemaSourceProvider<T>)reg.getProvider()).getSource(id);
- return Futures.transform(((SchemaSourceProvider<T>)it.next().getProvider()).getSource(id), new AsyncFunction<Optional<T>, Optional<T>>() {
+ return Futures.makeChecked(Futures.withFallback(f, new FutureFallback<T>() {
@Override
- public ListenableFuture<Optional<T>> apply(final Optional<T> input) throws Exception {
- if (input.isPresent()) {
- return Futures.immediateFuture(input);
- } else {
+ public ListenableFuture<T> create(final Throwable t) throws SchemaSourceException {
+ LOG.debug("Failed to acquire source from {}", reg, t);
+
+ if (it.hasNext()) {
return fetchSource(id, it);
}
- }
- });
- }
-
- private <T extends SchemaSourceRepresentation> ListenableFuture<Optional<T>> transformSchemaSource(final SourceIdentifier id, final Class<T> representation) {
- final Multimap<Class<?>, AbstractSchemaSourceRegistration> srcs = sources.get(id);
- if (srcs.isEmpty()) {
- return Futures.immediateFailedFuture(new SchemaSourceTransformationException(
- String.format("No providers producing a representation of %s registered", id)));
- }
-
- final Collection<SchemaTransformerRegistration> ts = transformers.get(representation);
- if (ts.isEmpty()) {
- return Futures.immediateFailedFuture(new SchemaSourceTransformationException(
- String.format("No transformers producing representation %s registered", representation)));
- }
- // Build up the candidate list
- final List<SchemaTransformerRegistration> candidates = new ArrayList<>();
- for (SchemaTransformerRegistration tr : ts) {
- final SchemaSourceTransformer<?, ?> t = tr.getInstance();
- final Class<?> i = t.getInputRepresentation();
- if (srcs.containsKey(i)) {
- candidates.add(tr);
- } else {
- LOG.debug("Provider for {} in {} not found, skipping transfomer {}", id, i, t);
+ throw new MissingSchemaSourceException("All available providers exhausted", id, t);
}
- }
+ }), FETCH_MAPPER);
+ }
- if (candidates.isEmpty()) {
- return Futures.immediateFailedFuture(new SchemaSourceTransformationException(
- String.format("No matching source/transformer pair for source %s representation %s found", id, representation)));
+ @Override
+ public <T extends SchemaSourceRepresentation> CheckedFuture<T, SchemaSourceException> getSchemaSource(final SourceIdentifier id, final Class<T> representation) {
+ 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));
}
- 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)
+ final ArrayList<AbstractSchemaSourceRegistration<?>> sortedSchemaSourceRegistrations = Lists.newArrayList(srcs.get(representation));
+ Collections.sort(sortedSchemaSourceRegistrations, SchemaProviderCostComparator.INSTANCE);
- /**
- * Obtain a SchemaSource is selected representation
- */
- protected <T extends SchemaSourceRepresentation> ListenableFuture<Optional<T>> getSchemaSource(final SourceIdentifier id, final Class<T> representation) {
- final Multimap<Class<?>, AbstractSchemaSourceRegistration> srcs = sources.get(id);
- if (srcs == null) {
- LOG.debug("No providers registered for source {}", id);
- return Futures.immediateFuture(Optional.<T>absent());
+ final Iterator<AbstractSchemaSourceRegistration<?>> regs = sortedSchemaSourceRegistrations.iterator();
+ if (!regs.hasNext()) {
+ return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(
+ new MissingSchemaSourceException("No providers for source " + id + " representation " + representation + " available", id));
}
- final Collection<AbstractSchemaSourceRegistration> candidates = srcs.get(representation);
- return Futures.transform(AbstractSchemaRepository.<T>fetchSource(id, candidates.iterator()), new AsyncFunction<Optional<T>, Optional<T>>() {
+ CheckedFuture<T, SchemaSourceException> fetchSourceFuture = fetchSource(id, regs);
+ // Add callback to notify cache listeners about encountered schema
+ Futures.addCallback(fetchSourceFuture, new FutureCallback<T>() {
@Override
- public ListenableFuture<Optional<T>> apply(final Optional<T> 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
+ public void onFailure(final Throwable t) {
+ LOG.trace("Skipping notification for encountered source {}, fetching source failed", id, t);
}
});
- }
- @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<Class<?>, AbstractSchemaSourceRegistration> m = sources.get(id);
+ private synchronized <T extends SchemaSourceRepresentation> void addSource(final PotentialSchemaSource<T> source, final AbstractSchemaSourceRegistration<T> reg) {
+ ListMultimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> m = sources.get(source.getSourceIdentifier());
if (m == null) {
- m = HashMultimap.create();
- sources.put(id, m);
+ m = ArrayListMultimap.create();
+ sources.put(source.getSourceIdentifier(), m);
}
- m.put(rep, reg);
+ m.put(source.getRepresentation(), reg);
+
+ final Collection<PotentialSchemaSource<?>> reps = Collections.<PotentialSchemaSource<?>>singleton(source);
+ for (SchemaListenerRegistration l : listeners) {
+ l.getInstance().schemaSourceRegistered(reps);
+ }
}
- private void removeSource(final SourceIdentifier id, final Class<?> rep, final SchemaSourceRegistration reg) {
- final Multimap<Class<?>, AbstractSchemaSourceRegistration> m = sources.get(id);
+ private synchronized <T extends SchemaSourceRepresentation> void removeSource(final PotentialSchemaSource<?> source, final SchemaSourceRegistration<?> reg) {
+ final Multimap<Class<? extends SchemaSourceRepresentation>, 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 <T extends SchemaSourceRepresentation> SchemaSourceRegistration registerSchemaSource(
- final SourceIdentifier identifier, final SchemaSourceProvider<? super T> provider, final Class<T> representation) {
- final AbstractSchemaSourceRegistration ret = new AbstractSchemaSourceRegistration(identifier, provider) {
+ public <T extends SchemaSourceRepresentation> SchemaSourceRegistration<T> registerSchemaSource(final SchemaSourceProvider<? super T> provider, final PotentialSchemaSource<T> source) {
+ final PotentialSchemaSource<T> src = source.cachedReference();
+
+ final AbstractSchemaSourceRegistration<T> ret = new AbstractSchemaSourceRegistration<T>(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<PotentialSchemaSource<?>> col = new ArrayList<>();
+ for (Multimap<Class<? extends SchemaSourceRepresentation>, 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<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();
+ }
+ }
}