a321f9147d3b76bbc90ef2ddfae74d2660a39385
[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.CheckedFuture;
15 import com.google.common.util.concurrent.Futures;
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 public CheckedFuture<? extends YangTextSchemaSource, SchemaSourceException> getSource(
188         final SourceIdentifier sourceIdentifier) {
189         final YangModuleInfo yangModuleInfo = sourceIdentifierToModuleInfo.get(sourceIdentifier);
190
191         if (yangModuleInfo == null) {
192             LOG.debug("Unknown schema source requested: {}, available sources: {}", sourceIdentifier, sourceIdentifierToModuleInfo.keySet());
193             return Futures
194                 .immediateFailedCheckedFuture(new SchemaSourceException("Unknown schema source: " + sourceIdentifier));
195         }
196
197         return Futures
198             .immediateCheckedFuture(YangTextSchemaSource.delegateForByteSource(sourceIdentifier, new ByteSource() {
199                 @Override public InputStream openStream() throws IOException {
200                         return yangModuleInfo.getModuleSourceStream();
201                 }
202             }));
203     }
204
205     private static class YangModuleInfoRegistration extends AbstractObjectRegistration<YangModuleInfo> {
206
207         private final ModuleInfoBackedContext context;
208
209         public YangModuleInfoRegistration(final YangModuleInfo instance, final ModuleInfoBackedContext context) {
210             super(instance);
211             this.context = context;
212         }
213
214         @Override
215         protected void removeRegistration() {
216             context.remove(this);
217         }
218
219     }
220
221     private void remove(final YangModuleInfoRegistration registration) {
222         // FIXME implement
223     }
224
225     @Override
226     public SchemaContext getSchemaContext() {
227         final Optional<SchemaContext> contextOptional = tryToCreateSchemaContext();
228         if (contextOptional.isPresent()) {
229             return contextOptional.get();
230         }
231         throw new IllegalStateException("Unable to recreate SchemaContext, error while parsing");
232     }
233 }