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