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