823d288a20703b0a055dd76b5de92a718af45906
[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.CheckedFuture;
14 import com.google.common.util.concurrent.Futures;
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.model.api.SchemaContext;
28 import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
29 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
30 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
31 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
32 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
33 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
34 import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 public class ModuleInfoBackedContext extends GeneratedClassLoadingStrategy
39         implements ModuleInfoRegistry, SchemaContextProvider, SchemaSourceProvider<YangTextSchemaSource> {
40
41     private final YangTextSchemaContextResolver ctxResolver = YangTextSchemaContextResolver.create("binding-context");
42
43     private ModuleInfoBackedContext(final ClassLoadingStrategy loadingStrategy) {
44         this.backingLoadingStrategy = loadingStrategy;
45     }
46
47     public static ModuleInfoBackedContext create() {
48         return new ModuleInfoBackedContext(getTCCLClassLoadingStrategy());
49     }
50
51     public static ModuleInfoBackedContext create(final ClassLoadingStrategy loadingStrategy) {
52         return new ModuleInfoBackedContext(loadingStrategy);
53     }
54
55     private static final Logger LOG = LoggerFactory.getLogger(ModuleInfoBackedContext.class);
56
57     private final ConcurrentMap<String, WeakReference<ClassLoader>> packageNameToClassLoader = new ConcurrentHashMap<>();
58     private final ConcurrentMap<SourceIdentifier, YangModuleInfo> sourceIdentifierToModuleInfo = 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     private boolean resolveModuleInfo(final Class<?> cls) {
94         try {
95             return resolveModuleInfo(BindingReflections.getModuleInfo(cls));
96         } catch (Exception e) {
97             throw new IllegalStateException(String.format("Failed to resolve module information for class %s", cls), e);
98         }
99     }
100
101     private boolean resolveModuleInfo(final YangModuleInfo moduleInfo) {
102
103         SourceIdentifier identifier = sourceIdentifierFrom(moduleInfo);
104         YangModuleInfo previous = sourceIdentifierToModuleInfo.putIfAbsent(identifier, moduleInfo);
105         ClassLoader moduleClassLoader = moduleInfo.getClass().getClassLoader();
106         try {
107             if (previous == null) {
108                 String modulePackageName = moduleInfo.getClass().getPackage().getName();
109                 packageNameToClassLoader.putIfAbsent(modulePackageName,
110                         new WeakReference<>(moduleClassLoader));
111                 ctxResolver.registerSource(toYangTextSource(identifier, moduleInfo));
112                 for (YangModuleInfo importedInfo : moduleInfo.getImportedModules()) {
113                     resolveModuleInfo(importedInfo);
114                 }
115             } else {
116                 return false;
117             }
118         } catch (Exception e) {
119             LOG.error("Not including {} in YANG sources because of error.", moduleInfo, e);
120         }
121         return true;
122     }
123
124     private static YangTextSchemaSource toYangTextSource(final SourceIdentifier identifier, final YangModuleInfo moduleInfo) {
125         return new YangTextSchemaSource(identifier) {
126
127             @Override
128             public InputStream openStream() throws IOException {
129                 return moduleInfo.getModuleSourceStream();
130             }
131
132             @Override
133             protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
134                 return toStringHelper;
135             }
136         };
137     }
138
139     private static SourceIdentifier sourceIdentifierFrom(final YangModuleInfo moduleInfo) {
140         return RevisionSourceIdentifier.create(moduleInfo.getName(), Optional.of(moduleInfo.getRevision()));
141     }
142
143     public void addModuleInfos(final Iterable<? extends YangModuleInfo> moduleInfos) {
144         for (YangModuleInfo yangModuleInfo : moduleInfos) {
145             registerModuleInfo(yangModuleInfo);
146         }
147     }
148
149     @Override
150     public ObjectRegistration<YangModuleInfo> registerModuleInfo(final YangModuleInfo yangModuleInfo) {
151         YangModuleInfoRegistration registration = new YangModuleInfoRegistration(yangModuleInfo, this);
152         resolveModuleInfo(yangModuleInfo);
153         return registration;
154     }
155
156     @Override public CheckedFuture<? extends YangTextSchemaSource, SchemaSourceException> getSource(
157         final SourceIdentifier sourceIdentifier) {
158         final YangModuleInfo yangModuleInfo = sourceIdentifierToModuleInfo.get(sourceIdentifier);
159
160         if (yangModuleInfo == null) {
161             LOG.debug("Unknown schema source requested: {}, available sources: {}", sourceIdentifier,
162                 sourceIdentifierToModuleInfo.keySet());
163             return Futures.immediateFailedCheckedFuture(new SchemaSourceException(
164                 "Unknown schema source: " + sourceIdentifier));
165         }
166
167         return Futures.immediateCheckedFuture(YangTextSchemaSource.delegateForByteSource(sourceIdentifier,
168             new ByteSource() {
169             @Override
170             public InputStream openStream() throws IOException {
171                 return yangModuleInfo.getModuleSourceStream();
172             }
173         }));
174     }
175
176     private static class YangModuleInfoRegistration extends AbstractObjectRegistration<YangModuleInfo> {
177
178         private final ModuleInfoBackedContext context;
179
180         public YangModuleInfoRegistration(final YangModuleInfo instance, final ModuleInfoBackedContext context) {
181             super(instance);
182             this.context = context;
183         }
184
185         @Override
186         protected void removeRegistration() {
187             context.remove(this);
188         }
189
190     }
191
192     private void remove(final YangModuleInfoRegistration registration) {
193         // FIXME implement
194     }
195
196     @Override
197     public SchemaContext getSchemaContext() {
198         final Optional<SchemaContext> contextOptional = tryToCreateSchemaContext();
199         if (contextOptional.isPresent()) {
200             return contextOptional.get();
201
202         }
203         throw new IllegalStateException("Unable to recreate SchemaContext, error while parsing");
204     }
205 }