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