d07edae14ee42283827ddf8c52cb6ce9d92aba44
[controller.git] / opendaylight / config / yang-jmx-generator-plugin / src / main / java / org / opendaylight / controller / config / yangjmxgenerator / plugin / ftl / RuntimeRegistratorFtlTemplate.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.config.yangjmxgenerator.plugin.ftl;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12 import static java.lang.String.format;
13
14 import java.io.Closeable;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.LinkedHashMap;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Map.Entry;
25 import java.util.Set;
26 import java.util.concurrent.atomic.AtomicInteger;
27
28 import org.opendaylight.controller.config.api.runtime.HierarchicalRuntimeBeanRegistration;
29 import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator;
30 import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
31 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation;
32 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation.Parameter;
33 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
34 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition;
35 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
36
37 import com.google.common.annotations.VisibleForTesting;
38 import com.google.common.collect.Lists;
39
40 public class RuntimeRegistratorFtlTemplate extends GeneralClassTemplate {
41
42     private RuntimeRegistratorFtlTemplate(RuntimeBeanEntry runtimeBeanEntry,
43             String name, List<Field> fields, List<MethodDefinition> methods) {
44         // TODO header
45         super(null, runtimeBeanEntry.getPackageName(), name, Collections
46                 .<String> emptyList(), Arrays.asList(Closeable.class
47                 .getCanonicalName()), fields, methods);
48     }
49
50     public static RuntimeBeanEntry findRoot(
51             Collection<RuntimeBeanEntry> runtimeBeanEntries) {
52         RuntimeBeanEntry result = null;
53         for (RuntimeBeanEntry rb : runtimeBeanEntries) {
54             if (rb.isRoot()) {
55                 if (result != null) {
56                     throw new IllegalArgumentException(
57                             "More than one root runtime bean found");
58                 }
59                 result = rb;
60             }
61         }
62         if (result != null) {
63             return result;
64         }
65         throw new IllegalArgumentException("No root runtime bean found");
66     }
67
68     private static String constructConstructorBody(
69             List<Field> constructorParameters) {
70         StringBuffer constructorBody = new StringBuffer();
71         for (Field field : constructorParameters) {
72             constructorBody.append("this.");
73             constructorBody.append(field.getName());
74             constructorBody.append("=");
75             constructorBody.append(field.getName());
76             constructorBody.append(";\n");
77         }
78         return constructorBody.toString();
79     }
80
81     // TODO Move to factory
82     /**
83      * Get registrator and n registration ftls where n is equal to total number
84      * of runtime beans in hierarchy.
85      */
86     public static Map<String, FtlTemplate> create(RuntimeBeanEntry rootRB) {
87         checkArgument(rootRB.isRoot(), "RuntimeBeanEntry must be root");
88         String registratorName = getJavaNameOfRuntimeRegistrator(rootRB);
89         List<MethodDefinition> methods = new ArrayList<>();
90         Field rootRuntimeBeanRegistratorField = new Field(
91                 Lists.newArrayList("final"),
92                 RootRuntimeBeanRegistrator.class.getName(),
93                 "rootRuntimeBeanRegistrator");
94         List<Field> constructorParameters = Lists
95                 .newArrayList(rootRuntimeBeanRegistratorField);
96         String constructorBody = constructConstructorBody(constructorParameters);
97         MethodDefinition constructor = MethodDefinition.createConstructor(
98                 registratorName, constructorParameters, constructorBody);
99         methods.add(constructor);
100
101         LinkedHashMap<String, RuntimeRegistratorFtlTemplate> RuntimeRegistratorFtlTemplates = createRegistrationHierarchy(
102                 rootRB, Collections.<String> emptySet());
103         RuntimeRegistratorFtlTemplate rootFtlFile = RuntimeRegistratorFtlTemplates
104                 .values().iterator().next();
105
106         {// add register(rootruntimemxbean)
107             String fullyQualifiedNameOfMXBean = FullyQualifiedNameHelper
108                     .getFullyQualifiedName(rootRB.getPackageName(), rootRB.getJavaNameOfRuntimeMXBean());
109             String childRegistratorFQN = rootFtlFile.getFullyQualifiedName();
110             Field rbParameter = new Field(fullyQualifiedNameOfMXBean, "rb");
111             StringBuffer registerBody = new StringBuffer();
112             registerBody.append(format("%s %s = this.%s.registerRoot(%s);\n",
113                     HierarchicalRuntimeBeanRegistration.class
114                             .getCanonicalName(), hierachchicalRegistration
115                             .getName(), rootRuntimeBeanRegistratorField
116                             .getName(), rbParameter.getName()));
117             registerBody.append(format("return new %s(%s);\n",
118                     rootFtlFile.getFullyQualifiedName(),
119                     hierachchicalRegistration.getName()));
120
121             MethodDefinition registerMethod = new MethodDefinition(
122                     childRegistratorFQN, "register",
123                     Arrays.asList(rbParameter), registerBody.toString());
124             methods.add(registerMethod);
125         }
126
127         MethodDefinition closeRegistrator = createCloseMethodToCloseField(rootRuntimeBeanRegistratorField);
128         methods.add(closeRegistrator);
129
130         // TODO add header
131         GeneralClassTemplate registrator = new GeneralClassTemplate(null,
132                 rootRB.getPackageName(), registratorName,
133                 Collections.<String> emptyList(), Arrays.asList(Closeable.class
134                         .getCanonicalName()), constructorParameters, methods);
135
136         checkState(RuntimeRegistratorFtlTemplates.containsKey(registrator
137                 .getTypeDeclaration().getName()) == false, "Name conflict: "
138                 + registrator.getTypeDeclaration().getName());
139         Map<String, FtlTemplate> result = new HashMap<>();
140         result.putAll(RuntimeRegistratorFtlTemplates);
141         result.put(registrator.getTypeDeclaration().getName(), registrator);
142         return result;
143     }
144
145     private static Field hierachchicalRegistration = new Field(
146             Lists.newArrayList("final"),
147             HierarchicalRuntimeBeanRegistration.class.getCanonicalName(),
148             "registration");
149
150     // TODO move to factory + RuntimeBeanEntry
151     /**
152      * Create ftls representing registrations. First registration is represents
153      * parent.
154      *
155      * @return map containing java class name as key, instance representing the
156      *         java file as value.
157      */
158     private static LinkedHashMap<String, RuntimeRegistratorFtlTemplate> createRegistrationHierarchy(
159             RuntimeBeanEntry parent, Set<String> occupiedKeys) {
160         LinkedHashMap<String, RuntimeRegistratorFtlTemplate> unorderedResult = new LinkedHashMap<>();
161         List<MethodDefinition> methods = new ArrayList<>();
162
163         // hierarchy of ON is created as follows:
164         // root RB: <domain>, type=RuntimeBean
165         // 1st RB in hierarchy: <domain>, type=RuntimeBean, <java name of leaf
166         // list>: key or counter
167         // n-th RB in hierarchy has same ON as n-1, with added <java name of
168         // leaf list>: key or counter
169         if (occupiedKeys.contains(parent.getJavaNamePrefix())) {
170             throw new IllegalArgumentException(
171                     "Name conflict in runtime bean hierarchy - java name found more than "
172                             + "once. Consider using java-name extension. Conflicting name: "
173                             + parent.getJavaNamePrefix());
174         }
175         Set<String> currentOccupiedKeys = new HashSet<>(occupiedKeys);
176         currentOccupiedKeys.add(parent.getJavaNamePrefix());
177
178         Field registratorsMapField = new Field(Arrays.asList("final"),
179                 TypeHelper.getGenericType(Map.class, String.class,
180                         AtomicInteger.class), "unkeyedMap", "new "
181                         + TypeHelper.getGenericType(HashMap.class,
182                                 String.class, AtomicInteger.class) + "()");
183
184         // create register methods for children
185         for (RuntimeBeanEntry child : parent.getChildren()) {
186             checkArgument(parent.getPackageName()
187                     .equals(child.getPackageName()), "Invalid package name");
188
189             // call itself recursively to generate child
190             // registrators/registrations
191             LinkedHashMap<String, RuntimeRegistratorFtlTemplate> childRegistratorMap = createRegistrationHierarchy(
192                     child, currentOccupiedKeys);
193             for (Entry<String, RuntimeRegistratorFtlTemplate> entry : childRegistratorMap
194                     .entrySet()) {
195                 if (unorderedResult.containsKey(entry.getKey())) {
196                     throw new IllegalStateException(
197                             "Conflicting name found while generating runtime registration:"
198                                     + entry.getKey());
199                 }
200                 unorderedResult.put(entry.getKey(), entry.getValue());
201             }
202
203             if (childRegistratorMap.size() > 0) {
204                 // first entry is the direct descendant according to the create
205                 // contract
206                 RuntimeRegistratorFtlTemplate childRegistrator = childRegistratorMap
207                         .values().iterator().next();
208                 StringBuffer body = new StringBuffer();
209                 String key, value;
210                 key = child.getJavaNamePrefix();
211                 body.append(format(
212                         "String key = \"%s\"; //TODO: check for conflicts\n",
213                         key));
214
215                 if (child.getKeyJavaName().isPresent()) {
216                     value = "bean.get" + child.getKeyJavaName().get() + "()";
217                     value = "String.valueOf(" + value + ")";
218                 } else {
219                     body.append("java.util.concurrent.atomic.AtomicInteger counter = unkeyedMap.get(key);\n"
220                             + "if (counter==null){\n"
221                             + "counter = new java.util.concurrent.atomic.AtomicInteger();\n"
222                             + "unkeyedMap.put(key, counter);\n" + "}\n");
223                     value = "String.valueOf(counter.incrementAndGet())";
224                 }
225                 body.append(format("String value = %s;\n", value));
226                 body.append(format("%s r = %s.register(key, value, bean);\n",
227                         HierarchicalRuntimeBeanRegistration.class
228                                 .getCanonicalName(), hierachchicalRegistration
229                                 .getName()));
230                 body.append(format("return new %s(r);",
231                         childRegistrator.getFullyQualifiedName()));
232
233                 Field param = new Field(Lists.newArrayList("final"),
234                         child.getJavaNameOfRuntimeMXBean(), "bean");
235                 MethodDefinition register = new MethodDefinition(
236                         Arrays.asList("synchronized"),
237                         childRegistrator.getFullyQualifiedName(), "register",
238                         Arrays.asList(param), Collections.<String> emptyList(),
239                         Collections.<Annotation> emptyList(), body.toString());
240                 methods.add(register);
241
242             }
243         }
244
245         // create parent registration
246         String createdName = getJavaNameOfRuntimeRegistration(parent.getJavaNamePrefix());
247
248         List<Field> constructorParameters = Arrays
249                 .asList(hierachchicalRegistration);
250         String constructorBody = constructConstructorBody(constructorParameters);
251
252         MethodDefinition constructor = MethodDefinition.createConstructor(
253                 createdName, constructorParameters, constructorBody);
254
255         MethodDefinition closeRegistrator = createCloseMethodToCloseField(hierachchicalRegistration);
256         methods.add(closeRegistrator);
257         methods.add(constructor);
258         List<Field> privateFields = Lists.newArrayList(registratorsMapField);
259         privateFields.addAll(constructorParameters);
260
261         RuntimeRegistratorFtlTemplate created = new RuntimeRegistratorFtlTemplate(
262                 parent, createdName, privateFields, methods);
263
264         LinkedHashMap<String, RuntimeRegistratorFtlTemplate> result = new LinkedHashMap<>();
265         result.put(created.getTypeDeclaration().getName(), created);
266         checkState(unorderedResult.containsKey(created.getTypeDeclaration()
267                 .getName()) == false, "Naming conflict: "
268                 + created.getTypeDeclaration().getName());
269         result.putAll(unorderedResult);
270         return result;
271     }
272
273     private static MethodDefinition createCloseMethodToCloseField(Field field) {
274         String body = field.getName() + ".close();";
275         // TODO Thrown exception breaks build
276         // return new MethodDefinition(Collections.<String> emptyList(), "void",
277         // "close", Collections.<Field> emptyList(),
278         // Arrays.asList(IOException.class.getCanonicalName()),
279         // Collections.<Annotation> emptyList(), body);
280         List<Annotation> annotations = Lists.newArrayList(new Annotation(
281                 "Override", Collections.<Parameter> emptyList()));
282         return new MethodDefinition(Collections.<String> emptyList(), "void",
283                 "close", Collections.<Field> emptyList(),
284                 Collections.<String> emptyList(), annotations, body);
285     }
286
287     @VisibleForTesting
288     public static String getJavaNameOfRuntimeRegistration(String javaNamePrefix) {
289         return javaNamePrefix + "RuntimeRegistration";
290     }
291
292     public static String getJavaNameOfRuntimeRegistrator(RuntimeBeanEntry rootRB) {
293         checkArgument(rootRB.isRoot(), "RuntimeBeanEntry must be root");
294         return rootRB.getJavaNamePrefix() + "RuntimeRegistrator";
295     }
296 }