Adjust to yangtools-2.0.0 changes
[mdsal.git] / binding / mdsal-binding-generator-impl / src / main / java / org / opendaylight / mdsal / binding / generator / impl / ModuleInfoBackedContext.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. 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.generator.impl;
9
10 import com.google.common.base.MoreObjects.ToStringHelper;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Strings;
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.generator.api.ClassLoadingStrategy;
22 import org.opendaylight.mdsal.binding.generator.api.ModuleInfoRegistry;
23 import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
24 import org.opendaylight.yangtools.concepts.ObjectRegistration;
25 import org.opendaylight.yangtools.util.ClassLoaderUtils;
26 import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
27 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
28 import org.opendaylight.yangtools.yang.common.Revision;
29 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
30 import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
31 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
32 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
33 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
34 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
35 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
36 import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 public class ModuleInfoBackedContext extends GeneratedClassLoadingStrategy
41         implements ModuleInfoRegistry, SchemaContextProvider, SchemaSourceProvider<YangTextSchemaSource> {
42
43     private final YangTextSchemaContextResolver ctxResolver = YangTextSchemaContextResolver.create("binding-context");
44
45     private ModuleInfoBackedContext(final ClassLoadingStrategy loadingStrategy) {
46         this.backingLoadingStrategy = loadingStrategy;
47     }
48
49     public static ModuleInfoBackedContext create() {
50         return new ModuleInfoBackedContext(getTCCLClassLoadingStrategy());
51     }
52
53     public static ModuleInfoBackedContext create(final ClassLoadingStrategy loadingStrategy) {
54         return new ModuleInfoBackedContext(loadingStrategy);
55     }
56
57     private static final Logger LOG = LoggerFactory.getLogger(ModuleInfoBackedContext.class);
58
59     private final ConcurrentMap<String, WeakReference<ClassLoader>> packageNameToClassLoader =
60             new ConcurrentHashMap<>();
61     private final ConcurrentMap<SourceIdentifier, YangModuleInfo> sourceIdentifierToModuleInfo =
62             new ConcurrentHashMap<>();
63
64     private final ClassLoadingStrategy backingLoadingStrategy;
65
66     @Override
67     public Class<?> loadClass(final String fullyQualifiedName) throws ClassNotFoundException {
68         final String modulePackageName = BindingReflections.getModelRootPackageName(fullyQualifiedName);
69         final WeakReference<ClassLoader> classLoaderRef = packageNameToClassLoader.get(modulePackageName);
70         if (classLoaderRef != null) {
71             final ClassLoader classLoader = classLoaderRef.get();
72             if (classLoader != null) {
73                 return ClassLoaderUtils.loadClass(classLoader, fullyQualifiedName);
74             }
75         }
76
77         if (backingLoadingStrategy == null) {
78             throw new ClassNotFoundException(fullyQualifiedName);
79         }
80
81         final Class<?> cls = backingLoadingStrategy.loadClass(fullyQualifiedName);
82         if (BindingReflections.isBindingClass(cls)) {
83             resolveModuleInfo(cls);
84         }
85
86         return cls;
87     }
88
89     // TODO finish schema parsing and expose as SchemaService
90     // Unite with current SchemaService
91     // Implement remove ModuleInfo to update SchemaContext
92
93     public Optional<SchemaContext> tryToCreateSchemaContext() {
94         return Optional.fromJavaUtil(ctxResolver.getSchemaContext());
95     }
96
97     private boolean resolveModuleInfo(final Class<?> cls) {
98         try {
99             return resolveModuleInfo(BindingReflections.getModuleInfo(cls));
100         } catch (Exception e) {
101             throw new IllegalStateException(String.format("Failed to resolve module information for class %s", cls), e);
102         }
103     }
104
105     private boolean resolveModuleInfo(final YangModuleInfo moduleInfo) {
106
107         SourceIdentifier identifier = sourceIdentifierFrom(moduleInfo);
108         YangModuleInfo previous = sourceIdentifierToModuleInfo.putIfAbsent(identifier, moduleInfo);
109         ClassLoader moduleClassLoader = moduleInfo.getClass().getClassLoader();
110         try {
111             if (previous == null) {
112                 String modulePackageName = moduleInfo.getClass().getPackage().getName();
113                 packageNameToClassLoader.putIfAbsent(modulePackageName,
114                         new WeakReference<>(moduleClassLoader));
115                 ctxResolver.registerSource(toYangTextSource(identifier, moduleInfo));
116                 for (YangModuleInfo importedInfo : moduleInfo.getImportedModules()) {
117                     resolveModuleInfo(importedInfo);
118                 }
119             } else {
120                 return false;
121             }
122         } catch (Exception e) {
123             LOG.error("Not including {} in YANG sources because of error.", moduleInfo, e);
124         }
125         return true;
126     }
127
128     private static YangTextSchemaSource toYangTextSource(final SourceIdentifier identifier,
129             final YangModuleInfo moduleInfo) {
130         return new YangTextSchemaSource(identifier) {
131
132             @Override
133             public InputStream openStream() throws IOException {
134                 return moduleInfo.getModuleSourceStream();
135             }
136
137             @Override
138             protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
139                 return toStringHelper;
140             }
141         };
142     }
143
144     private static SourceIdentifier sourceIdentifierFrom(final YangModuleInfo moduleInfo) {
145         return RevisionSourceIdentifier.create(moduleInfo.getName(), Revision.ofNullable(moduleInfo.getRevision()));
146     }
147
148     public void addModuleInfos(final Iterable<? extends YangModuleInfo> moduleInfos) {
149         for (YangModuleInfo yangModuleInfo : moduleInfos) {
150             registerModuleInfo(yangModuleInfo);
151         }
152     }
153
154     @Override
155     public ObjectRegistration<YangModuleInfo> registerModuleInfo(final YangModuleInfo yangModuleInfo) {
156         YangModuleInfoRegistration registration = new YangModuleInfoRegistration(yangModuleInfo, this);
157         resolveModuleInfo(yangModuleInfo);
158         return registration;
159     }
160
161     @Override
162     public CheckedFuture<? extends YangTextSchemaSource, SchemaSourceException> getSource(
163         final SourceIdentifier sourceIdentifier) {
164         final YangModuleInfo yangModuleInfo = sourceIdentifierToModuleInfo.get(sourceIdentifier);
165
166         if (yangModuleInfo == null) {
167             LOG.debug("Unknown schema source requested: {}, available sources: {}", sourceIdentifier,
168                 sourceIdentifierToModuleInfo.keySet());
169             return Futures.immediateFailedCheckedFuture(new SchemaSourceException(
170                 "Unknown schema source: " + sourceIdentifier));
171         }
172
173         return Futures.immediateCheckedFuture(YangTextSchemaSource.delegateForByteSource(sourceIdentifier,
174             new ByteSource() {
175             @Override
176             public InputStream openStream() throws IOException {
177                 return yangModuleInfo.getModuleSourceStream();
178             }
179         }));
180     }
181
182     private static class YangModuleInfoRegistration extends AbstractObjectRegistration<YangModuleInfo> {
183
184         private final ModuleInfoBackedContext context;
185
186         public YangModuleInfoRegistration(final YangModuleInfo instance, final ModuleInfoBackedContext context) {
187             super(instance);
188             this.context = context;
189         }
190
191         @Override
192         protected void removeRegistration() {
193             context.remove(this);
194         }
195
196     }
197
198     private void remove(final YangModuleInfoRegistration registration) {
199         // FIXME implement
200     }
201
202     @Override
203     public SchemaContext getSchemaContext() {
204         final Optional<SchemaContext> contextOptional = tryToCreateSchemaContext();
205         if (contextOptional.isPresent()) {
206             return contextOptional.get();
207
208         }
209         throw new IllegalStateException("Unable to recreate SchemaContext, error while parsing");
210     }
211 }