Merge "Refactor DOMDataBrokerImpl"
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / main / java / org / opendaylight / controller / sal / binding / codegen / impl / DefaultRuntimeCodeGenerator.java
1 /*
2  * Copyright (c) 2013 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.controller.sal.binding.codegen.impl;
9
10 import com.google.common.base.Supplier;
11 import com.google.common.collect.ImmutableSet;
12 import com.google.common.collect.ImmutableSet.Builder;
13 import java.lang.reflect.Method;
14 import java.util.Map;
15 import javassist.CannotCompileException;
16 import javassist.ClassPool;
17 import javassist.CtClass;
18 import javassist.CtMethod;
19 import javassist.NotFoundException;
20 import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification;
21 import org.opendaylight.yangtools.sal.binding.generator.util.ClassGenerator;
22 import org.opendaylight.yangtools.sal.binding.generator.util.MethodGenerator;
23 import org.opendaylight.yangtools.util.ClassLoaderUtils;
24 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
25 import org.opendaylight.yangtools.yang.binding.Notification;
26 import org.opendaylight.yangtools.yang.binding.NotificationListener;
27 import org.opendaylight.yangtools.yang.binding.RpcImplementation;
28 import org.opendaylight.yangtools.yang.binding.RpcService;
29 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
30
31 final class DefaultRuntimeCodeGenerator extends AbstractRuntimeCodeGenerator {
32
33     DefaultRuntimeCodeGenerator(final ClassPool pool) {
34         super(pool);
35     }
36
37     @Override
38     protected <T extends RpcService> Supplier<T> directProxySupplier(final Class<T> serviceType) {
39         return new Supplier<T>() {
40             @SuppressWarnings("unchecked")
41             @Override
42             public T get() {
43                 final String proxyName = RuntimeCodeSpecification.getDirectProxyName(serviceType);
44
45                 final Class<?> potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(proxyName);
46                 if (potentialClass != null) {
47                     try {
48                         return (T)potentialClass.newInstance();
49                     } catch (InstantiationException | IllegalAccessException e) {
50                         throw new IllegalStateException("Failed to instantiate class " + potentialClass.getName(), e);
51                     }
52                 }
53
54                 final CtClass supertype = utils.asCtClass(serviceType);
55                 final String directProxyName = RuntimeCodeSpecification.getDirectProxyName(serviceType);
56
57                 final CtClass createdCls;
58                 try {
59                     createdCls = utils.createClass(directProxyName, supertype, new ClassGenerator() {
60                         @Override
61                         public void process(final CtClass cls) throws CannotCompileException {
62                             utils.field(cls, RuntimeCodeSpecification.DELEGATE_FIELD, serviceType);
63                             utils.implementsType(cls, utils.asCtClass(RpcImplementation.class));
64                             utils.implementMethodsFrom(cls, supertype, new MethodGenerator() {
65                                 @Override
66                                 public void process(final CtMethod method) throws CannotCompileException {
67                                     final StringBuilder sb = new StringBuilder("\n");
68                                     sb.append("{\n");
69                                     sb.append("    if (").append(RuntimeCodeSpecification.DELEGATE_FIELD).append(" == null) {\n");
70                                     sb.append("        throw new java.lang.IllegalStateException(\"No default provider is available\");\n");
71                                     sb.append("    }\n");
72                                     sb.append("    return ($r) ").append(RuntimeCodeSpecification.DELEGATE_FIELD).append('.').append(method.getName()).append("($$);\n");
73                                     sb.append("}\n");
74                                     method.setBody(sb.toString());
75                                 }
76                             });
77
78                             // FIXME: copy this one...
79                             utils.implementMethodsFrom(cls, utils.asCtClass(RpcImplementation.class), new MethodGenerator() {
80                                 @Override
81                                 public void process(final CtMethod method) throws CannotCompileException {
82                                     final StringBuilder sb = new StringBuilder("\n");
83                                     sb.append("{\n");
84                                     sb.append("    throw new java.lang.IllegalStateException(\"No provider is processing supplied message\");\n");
85                                     sb.append("    return ($r) null;\n");
86                                     sb.append("}\n");
87                                     method.setBody(sb.toString());
88                                 }
89                             });
90                         }
91                     });
92                 } catch (CannotCompileException e) {
93                     throw new IllegalStateException("Failed to create class " + directProxyName, e);
94                 }
95
96                 final Class<?> c;
97                 try {
98                     c = createdCls.toClass(serviceType.getClassLoader(), serviceType.getProtectionDomain());
99                 } catch (CannotCompileException e) {
100                     throw new IllegalStateException(String.format("Failed to create class %s", createdCls), e);
101                 }
102
103                 try {
104                     return (T) c.newInstance();
105                 } catch (InstantiationException | IllegalAccessException e) {
106                     throw new IllegalStateException(String.format("Failed to instantiated class %s", c), e);
107                 }
108             }
109         };
110     }
111
112     @Override
113     protected <T extends RpcService> Supplier<T> routerSupplier(final Class<T> serviceType, final RpcServiceMetadata metadata) {
114         return new Supplier<T>() {
115             @SuppressWarnings("unchecked")
116             @Override
117             public T get() {
118                 final CtClass supertype = utils.asCtClass(serviceType);
119                 final String routerName = RuntimeCodeSpecification.getRouterName(serviceType);
120                 final Class<?> potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(routerName);
121                 if (potentialClass != null) {
122                     try {
123                         return (T)potentialClass.newInstance();
124                     } catch (InstantiationException | IllegalAccessException e) {
125                         throw new IllegalStateException("Failed to instantiate class", e);
126                     }
127                 }
128
129                 final CtClass targetCls;
130                 try {
131                     targetCls = utils.createClass(routerName, supertype, new ClassGenerator() {
132                         @Override
133                         public void process(final CtClass cls) throws CannotCompileException {
134                             utils.field(cls, RuntimeCodeSpecification.DELEGATE_FIELD, serviceType);
135                             //utils.field(cls, REMOTE_INVOKER_FIELD,iface);
136                             utils.implementsType(cls, utils.asCtClass(RpcImplementation.class));
137
138                             for (final Class<? extends BaseIdentity> ctx : metadata.getContexts()) {
139                                 utils.field(cls, RuntimeCodeSpecification.getRoutingTableField(ctx), Map.class);
140                             }
141
142                             utils.implementMethodsFrom(cls, supertype, new MethodGenerator() {
143                                 @Override
144                                 public void process(final CtMethod method) throws CannotCompileException {
145                                     final int ptl;
146                                     try {
147                                         ptl = method.getParameterTypes().length;
148                                     } catch (NotFoundException e) {
149                                         throw new CannotCompileException(e);
150                                     }
151                                     final StringBuilder sb = new StringBuilder();
152
153                                     switch (ptl) {
154                                     case 0:
155                                         sb.append("return ($r) ").append(RuntimeCodeSpecification.DELEGATE_FIELD).append('.').append(method.getName()).append("($$);");
156                                         break;
157                                     case 1:
158                                         final RpcMetadata rpcMeta = metadata.getRpcMethod(method.getName());
159                                         final String rtGetter = rpcMeta.getInputRouteGetter().getName();
160                                         final String stName = supertype.getName();
161
162                                         sb.append('\n');
163                                         sb.append("{\n");
164                                         sb.append("    if ($1 == null) {\n");
165                                         sb.append("        throw new IllegalArgumentException(\"RPC input must not be null and must contain a value for field ").append(rtGetter).append("\");\n");
166                                         sb.append("    }\n");
167                                         sb.append("    if ($1.").append(rtGetter).append("() == null) {\n");
168                                         sb.append("        throw new IllegalArgumentException(\"Field ").append(rtGetter).append(" must not be null\");\n");
169                                         sb.append("    }\n");
170
171                                         sb.append("    final org.opendaylight.yangtools.yang.binding.InstanceIdentifier identifier = $1.").append(rtGetter).append("()");
172                                         if (rpcMeta.isRouteEncapsulated()) {
173                                             sb.append(".getValue()");
174                                         }
175                                         sb.append(";\n");
176
177                                         sb.append("    ").append(supertype.getName()).append(" instance = (").append(stName).append(") ").append(RuntimeCodeSpecification.getRoutingTableField(rpcMeta.getContext())).append(".get(identifier);\n");
178                                         sb.append("    if (instance == null) {\n");
179                                         sb.append("        instance = ").append(RuntimeCodeSpecification.DELEGATE_FIELD).append(";\n");
180                                         sb.append("    }\n");
181
182                                         sb.append("    if (instance == null) {\n");
183                                         sb.append("        throw new java.lang.IllegalStateException(\"No routable provider is processing routed message for \" + String.valueOf(identifier));\n");
184                                         sb.append("    }\n");
185                                         sb.append("    return ($r) instance.").append(method.getName()).append("($$);\n");
186                                         sb.append('}');
187                                         break;
188                                     default:
189                                         throw new CannotCompileException(String.format("Unsupported parameters length %s", ptl));
190                                     }
191
192                                     method.setBody(sb.toString());
193                                 }
194                             });
195
196                             // FIXME: move this into a template class
197                             utils.implementMethodsFrom(cls, utils.asCtClass(RpcImplementation.class), new MethodGenerator() {
198                                 @Override
199                                 public void process(final CtMethod method) throws CannotCompileException {
200                                     final StringBuilder sb = new StringBuilder("\n");
201                                     sb.append("{\n");
202                                     sb.append("    throw new java.lang.IllegalStateException(\"No provider is processing supplied message\");\n");
203                                     sb.append("    return ($r) null;\n");
204                                     sb.append("}\n");
205
206                                     method.setBody(sb.toString());
207                                 }
208                             });
209                         }
210                     });
211                 } catch (CannotCompileException e) {
212                     throw new IllegalStateException("Failed to create class " + routerName, e);
213                 }
214
215                 final Class<?> c;
216                 try {
217                     c = targetCls.toClass(serviceType.getClassLoader(), serviceType.getProtectionDomain());
218                 } catch (CannotCompileException e) {
219                     throw new IllegalStateException(String.format("Failed to compile class %s", targetCls), e);
220                 }
221
222                 try {
223                     return (T)c.newInstance();
224                 } catch (InstantiationException | IllegalAccessException e) {
225                     throw new IllegalStateException(String.format("Failed to instantiate class %s", c), e);
226                 }
227             }
228         };
229     }
230
231     @SuppressWarnings("unchecked")
232     @Override
233     protected RuntimeGeneratedInvokerPrototype generateListenerInvoker(final Class<? extends NotificationListener> listenerType) {
234         final String invokerName = RuntimeCodeSpecification.getInvokerName(listenerType);
235         final CtClass targetCls;
236
237         // Builder for a set of supported types. Filled while the target class is being generated
238         final Builder<Class<? extends Notification>> b = ImmutableSet.builder();
239
240         try {
241             targetCls = utils.createClass(invokerName, getBrokerNotificationListener(), new ClassGenerator() {
242                 @Override
243                 public void process(final CtClass cls) throws CannotCompileException {
244                     utils.field(cls, RuntimeCodeSpecification.DELEGATE_FIELD, listenerType);
245                     utils.implementMethodsFrom(cls, getBrokerNotificationListener(), new MethodGenerator() {
246                         @Override
247                         public void process(final CtMethod method) throws CannotCompileException {
248                             final StringBuilder sb = new StringBuilder("\n");
249
250                             sb.append("{\n");
251
252                             for (Method m : listenerType.getMethods()) {
253                                 if (BindingReflections.isNotificationCallback(m)) {
254                                     final Class<?> argType = m.getParameterTypes()[0];
255
256                                     // populates builder above
257                                     b.add((Class<? extends Notification>) argType);
258
259                                     sb.append("    if ($1 instanceof ").append(argType.getName()).append(") {\n");
260                                     sb.append("        ").append(RuntimeCodeSpecification.DELEGATE_FIELD).append('.').append(m.getName()).append("((").append(argType.getName()).append(") $1);\n");
261                                     sb.append("        return null;\n");
262                                     sb.append("    } else ");
263                                 }
264                             }
265
266                             sb.append("    return null;\n");
267                             sb.append("}\n");
268                             method.setBody(sb.toString());
269                         }
270                     });
271                 }
272             });
273         } catch (CannotCompileException e) {
274             throw new IllegalStateException("Failed to create class " + invokerName, e);
275         }
276
277         final Class<?> finalClass;
278         try {
279             finalClass = targetCls.toClass(listenerType.getClassLoader(), listenerType.getProtectionDomain());
280         } catch (CannotCompileException e) {
281             throw new IllegalStateException(String.format("Failed to compile class %s", targetCls), e);
282         }
283
284         return new RuntimeGeneratedInvokerPrototype(b.build(), (Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener<?>>) finalClass);
285     }
286 }