Get rid of CheckedFutures
[mdsal.git] / binding2 / mdsal-binding2-runtime / src / main / java / org / opendaylight / mdsal / binding / javav2 / runtime / context / ModuleInfoBackedContext.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.binding.javav2.runtime.context;
9
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.MoreObjects.ToStringHelper;
12 import com.google.common.base.Optional;
13 import com.google.common.io.ByteSource;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.lang.ref.WeakReference;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.ConcurrentMap;
21 import org.opendaylight.mdsal.binding.javav2.generator.api.ClassLoadingStrategy;
22 import org.opendaylight.mdsal.binding.javav2.generator.api.ModuleInfoRegistry;
23 import org.opendaylight.mdsal.binding.javav2.generator.impl.GeneratedClassLoadingStrategy;
24 import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
25 import org.opendaylight.mdsal.binding.javav2.spec.runtime.YangModuleInfo;
26 import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
27 import org.opendaylight.yangtools.concepts.ObjectRegistration;
28 import org.opendaylight.yangtools.util.ClassLoaderUtils;
29 import org.opendaylight.yangtools.yang.common.Revision;
30 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
31 import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
32 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
33 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
34 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
35 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
36 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
37 import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * Module info context.
43  */
44 @Beta
45 public class ModuleInfoBackedContext extends GeneratedClassLoadingStrategy
46         implements ModuleInfoRegistry, SchemaContextProvider, SchemaSourceProvider<YangTextSchemaSource> {
47
48     private static final Logger LOG = LoggerFactory.getLogger(ModuleInfoBackedContext.class);
49
50     private final YangTextSchemaContextResolver ctxResolver = YangTextSchemaContextResolver.create("binding-context");
51     private final ConcurrentMap<String, WeakReference<ClassLoader>> packageNameToClassLoader =
52             new ConcurrentHashMap<>();
53     private final ConcurrentMap<SourceIdentifier, YangModuleInfo> sourceIdentifierToModuleInfo =
54             new ConcurrentHashMap<>();
55     private final ClassLoadingStrategy backingLoadingStrategy;
56
57
58     private ModuleInfoBackedContext(final ClassLoadingStrategy loadingStrategy) {
59         this.backingLoadingStrategy = loadingStrategy;
60     }
61
62     /**
63      * Create new module info context.
64      *
65      * @return new module info context
66      */
67     public static ModuleInfoBackedContext create() {
68         return new ModuleInfoBackedContext(getTCCLClassLoadingStrategy());
69     }
70
71     /**
72      * Create new module info context based on specific loading strategy.
73      *
74      * @param loadingStrategy
75      *            - specific loading strategy
76      * @return new module info cotext based on specific loading strategy
77      */
78     public static ModuleInfoBackedContext create(final ClassLoadingStrategy loadingStrategy) {
79         return new ModuleInfoBackedContext(loadingStrategy);
80     }
81
82     @Override
83     public Class<?> loadClass(final String fullyQualifiedName) throws ClassNotFoundException {
84         final String modulePackageName = BindingReflections.getModelRootPackageName(fullyQualifiedName);
85         final WeakReference<ClassLoader> classLoaderRef = packageNameToClassLoader.get(modulePackageName);
86         if (classLoaderRef != null) {
87             final ClassLoader classLoader = classLoaderRef.get();
88             if (classLoader != null) {
89                 return ClassLoaderUtils.loadClass(classLoader, fullyQualifiedName);
90             }
91         }
92
93         if (backingLoadingStrategy == null) {
94             throw new ClassNotFoundException(fullyQualifiedName);
95         }
96
97         final Class<?> cls = backingLoadingStrategy.loadClass(fullyQualifiedName);
98         if (BindingReflections.isBindingClass(cls)) {
99             resolveModuleInfo(cls);
100         }
101
102         return cls;
103     }
104
105     // TODO finish schema parsing and expose as SchemaService
106     // Unite with current SchemaService
107     // Implement remove ModuleInfo to update SchemaContext
108
109     /**
110      * Resolving of schema context.
111      *
112      * @return optional of schema context
113      */
114     public Optional<SchemaContext> tryToCreateSchemaContext() {
115         return Optional.fromJavaUtil(ctxResolver.getSchemaContext());
116     }
117
118     private boolean resolveModuleInfo(final Class<?> cls) {
119         try {
120             return resolveModuleInfo(BindingReflections.getModuleInfo(cls));
121         } catch (final Exception e) {
122             throw new IllegalStateException(String.format("Failed to resolve module information for class %s", cls), e);
123         }
124     }
125
126     private boolean resolveModuleInfo(final YangModuleInfo moduleInfo) {
127
128         final SourceIdentifier identifier = sourceIdentifierFrom(moduleInfo);
129         final YangModuleInfo previous = sourceIdentifierToModuleInfo.putIfAbsent(identifier, moduleInfo);
130         final ClassLoader moduleClassLoader = moduleInfo.getClass().getClassLoader();
131         try {
132             if (previous == null) {
133                 final String modulePackageName = moduleInfo.getClass().getPackage().getName();
134                 packageNameToClassLoader.putIfAbsent(modulePackageName,
135                         new WeakReference<>(moduleClassLoader));
136                 ctxResolver.registerSource(toYangTextSource(identifier, moduleInfo));
137                 for (final YangModuleInfo importedInfo : moduleInfo.getImportedModules()) {
138                     resolveModuleInfo(importedInfo);
139                 }
140             } else {
141                 return false;
142             }
143         } catch (final Exception e) {
144             LOG.error("Not including {} in YANG sources because of error.", moduleInfo, e);
145         }
146         return true;
147     }
148
149     private static YangTextSchemaSource toYangTextSource(final SourceIdentifier identifier, final YangModuleInfo moduleInfo) {
150         return new YangTextSchemaSource(identifier) {
151
152             @Override
153             public InputStream openStream() throws IOException {
154                 return moduleInfo.getModuleSourceStream();
155             }
156
157             @Override
158             protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
159                 return toStringHelper;
160             }
161         };
162     }
163
164     private static SourceIdentifier sourceIdentifierFrom(final YangModuleInfo moduleInfo) {
165         return RevisionSourceIdentifier.create(moduleInfo.getName(), Revision.ofNullable(moduleInfo.getRevision()));
166     }
167
168     /**
169      * Add new module info into context.
170      *
171      * @param moduleInfos
172      *            - new module info
173      */
174     public void addModuleInfos(final Iterable<? extends YangModuleInfo> moduleInfos) {
175         for (final YangModuleInfo yangModuleInfo : moduleInfos) {
176             registerModuleInfo(yangModuleInfo);
177         }
178     }
179
180     @Override
181     public ObjectRegistration<YangModuleInfo> registerModuleInfo(final YangModuleInfo yangModuleInfo) {
182         final YangModuleInfoRegistration registration = new YangModuleInfoRegistration(yangModuleInfo, this);
183         resolveModuleInfo(yangModuleInfo);
184         return registration;
185     }
186
187     @Override
188     public ListenableFuture<? extends YangTextSchemaSource> getSource(
189         final SourceIdentifier sourceIdentifier) {
190         final YangModuleInfo yangModuleInfo = sourceIdentifierToModuleInfo.get(sourceIdentifier);
191
192         if (yangModuleInfo == null) {
193             LOG.debug("Unknown schema source requested: {}, available sources: {}", sourceIdentifier,
194                 sourceIdentifierToModuleInfo.keySet());
195             return Futures.immediateFailedFuture(new SchemaSourceException("Unknown schema source: "
196                 + sourceIdentifier));
197         }
198
199         return Futures.immediateFuture(YangTextSchemaSource.delegateForByteSource(sourceIdentifier, new ByteSource() {
200             @Override
201             public InputStream openStream() throws IOException {
202                 return yangModuleInfo.getModuleSourceStream();
203             }
204         }));
205     }
206
207     private static class YangModuleInfoRegistration extends AbstractObjectRegistration<YangModuleInfo> {
208
209         private final ModuleInfoBackedContext context;
210
211         public YangModuleInfoRegistration(final YangModuleInfo instance, final ModuleInfoBackedContext context) {
212             super(instance);
213             this.context = context;
214         }
215
216         @Override
217         protected void removeRegistration() {
218             context.remove(this);
219         }
220
221     }
222
223     private void remove(final YangModuleInfoRegistration registration) {
224         // FIXME implement
225     }
226
227     @Override
228     public SchemaContext getSchemaContext() {
229         final Optional<SchemaContext> contextOptional = tryToCreateSchemaContext();
230         if (contextOptional.isPresent()) {
231             return contextOptional.get();
232         }
233         throw new IllegalStateException("Unable to recreate SchemaContext, error while parsing");
234     }
235 }