6307d13653a3af837a183dac85a96fa66faf409a
[appc.git] / appc-client / code-generator / src / main / java / org / onap / appc / tools / generator / extensions / YangContextBuilderImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Copyright (C) 2017 Amdocs
8  * =============================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  * 
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  * 
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * 
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  * ============LICENSE_END=========================================================
23  */
24
25 package org.onap.appc.tools.generator.extensions;
26
27 import com.google.common.base.Optional;
28 import org.onap.appc.tools.generator.api.ContextBuilder;
29 import org.opendaylight.yangtools.yang.model.api.Module;
30 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
31 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
32 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
33 import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver;
34
35 import java.io.IOException;
36 import java.lang.reflect.Modifier;
37 import java.lang.reflect.Proxy;
38 import java.net.URL;
39 import java.util.Arrays;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.Map;
43 import java.util.Set;
44
45 public class YangContextBuilderImpl implements ContextBuilder {
46
47     @Override
48     public Map<String, Object> buildContext(URL sourceYangURL, String contextConf) throws IOException {
49
50
51         Optional<SchemaContext> sc = null;
52         try ( YangTextSchemaContextResolver yangContextResolver =
53               YangTextSchemaContextResolver.create("yang-context-resolver")) {
54             yangContextResolver.registerSource(sourceYangURL);
55             sc = yangContextResolver.getSchemaContext();
56         } catch (SchemaSourceException | IOException | YangSyntaxErrorException e) {
57             throw new IOException(String.format("Exception occurred while processing sourceFileName %s ",sourceYangURL),e);
58         }
59
60         Map<String, Object> map = new HashMap<>();
61         if ( null != sc && sc.isPresent()) {
62             Set<Module> modules = sc.get().getModules();
63             for (final Module module : modules) {
64
65                 Module proxyModule = (new PackagePrivateReflectBug()).buildProxyObjects(module);
66                 map.put("module", proxyModule);
67             }
68         }
69
70         return map;
71     }
72
73
74     /**
75      * The Wrap Proxy uses java Proxy to work around bug JDK-8193172.  The Issue is that if a super class is package
76      * private its public methods cause IllegalAccessException when using java reflect to access those methods from a
77      * subclass which is package public.
78      * <p>
79      * Example exception:
80      * Caused by: java.lang.IllegalAccessException: Class freemarker.ext.beans.BeansWrapper cannot access a
81      * member of class org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.AbstractEffectiveModule
82      * with modifiers "public final"
83      */
84     private static class  PackagePrivateReflectBug {
85
86         /**
87          * Recursive method that traverses the yang model and wraps objects that extend package private classes
88          * with java proxy objects.
89          * @param candidateObj
90          * @param <T>
91          * @return
92          */
93         private <T> T buildProxyObjects(Object candidateObj) {
94
95             //can't proxy null so just return
96             if (candidateObj == null) {
97                 return null;
98             }
99
100
101             Class<?> candidateClass = candidateObj.getClass();
102
103             //prevent a recursive proxy
104             if (Proxy.isProxyClass(candidateClass)) {
105                 return (T) candidateObj;
106             }
107
108             //Can�t create a proxy an Array,  so replace each
109             //element the array with a proxy if needed.
110             if (candidateClass.isArray()) {
111                 Object[] sourceArray = (Object[]) candidateObj;
112                 for (int i = 0; i < sourceArray.length; i++) {
113                     sourceArray[i] = buildProxyObjects(sourceArray[i]);
114                 }
115                 return (T) candidateObj;
116             }
117
118             //Evaluate if this class must be wrapped in a proxy or not.
119             if (!isCandidateForProxy(candidateClass)) {
120                 return (T) candidateObj;
121             }
122
123             //Collect the interfaces for the proxy.  Proxy only work with classes
124             //that implement interfaces if there are none the obj cannot be wrapped
125             //with a proxy.
126             HashSet<Class<?>> interfaceSet = new HashSet<>();
127             collectInterfaces(candidateClass, interfaceSet);
128             if (interfaceSet.isEmpty()) {
129                 return (T) candidateObj;
130             }
131             Class<?>[] interfaces = new Class<?>[interfaceSet.size()];
132             interfaceSet.toArray(interfaces);
133
134             //wrap the Object in a proxy
135             return (T) Proxy.newProxyInstance(
136                     candidateClass.getClassLoader(),
137                     interfaces,
138                     (proxy, method, args) -> {
139                         Object returnObj = method.invoke(candidateObj, args);
140                         returnObj = buildProxyObjects(returnObj);
141                         return returnObj;
142                     }
143             );
144         }
145
146
147         /**
148          * This method determines if the specified class is a candidate for proxy.
149          *
150          * @param fromClass
151          * @return true - if the specifed class is a Candidate.
152          */
153         private boolean isCandidateForProxy(Class<?> fromClass) {
154
155             //a
156             Class<?>[] includeClasses = {
157                     java.util.Collection.class,
158                     java.util.Map.class,
159                     org.opendaylight.yangtools.yang.model.api.Module.class
160
161             };
162
163             for (Class<?> toClass : includeClasses) {
164                 if (toClass.isAssignableFrom(fromClass)) {
165                     return true;
166                 }
167             }
168
169             if (isExtendsPackagePrivateSuperClass(fromClass)) {
170                 return true;
171             }
172
173             return false;
174         }
175
176         /**
177          * Test if any of the super packages are package private.
178          *
179          * @param clazz
180          * @return
181          */
182         private boolean isExtendsPackagePrivateSuperClass(Class<?> clazz) {
183             if (Object.class.equals(clazz)) {
184                 return false;
185             }
186
187             int classModifiers = clazz.getModifiers();
188             if (Modifier.isPublic(classModifiers)) {
189                 return isExtendsPackagePrivateSuperClass(clazz.getSuperclass());
190             }
191
192             return true;
193         }
194
195         /**
196          * Collect all of the interfaces this class can be cast too.  Tavers the class hierarchy to include interfaces
197          * from the super classes.
198          *
199          * @param clazz
200          * @param classSet
201          */
202         private void collectInterfaces(Class<?> clazz, Set<Class<?>> classSet) {
203             Class<?>[] interfaces = clazz.getInterfaces();
204             classSet.addAll(Arrays.asList(interfaces));
205             Class<?> superClass = clazz.getSuperclass();
206
207     //
208             if (!Object.class.equals(superClass)) {
209                 collectInterfaces(superClass, classSet);
210             }
211         }
212     }
213 }