2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.config.yangjmxgenerator.plugin.gofactory;
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.lang.String.format;
13 import com.google.common.base.Joiner;
14 import com.google.common.base.Optional;
15 import java.util.ArrayList;
16 import java.util.HashMap;
17 import java.util.LinkedHashMap;
18 import java.util.List;
20 import org.opendaylight.controller.config.api.DependencyResolver;
21 import org.opendaylight.controller.config.api.ModuleIdentifier;
22 import org.opendaylight.controller.config.api.annotations.Description;
23 import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator;
24 import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
25 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.AbstractModuleTemplate;
26 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory;
27 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation;
28 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.IdentityRefModuleField;
29 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Method;
30 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField;
31 import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.FullyQualifiedName;
32 import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.GeneratedObject;
33 import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.GeneratedObjectBuilder;
34 import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.JavaFileInputBuilder;
35 import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.TypeName;
36 import org.opendaylight.yangtools.yang.common.QName;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 public class AbsModuleGeneratedObjectFactory {
42 public GeneratedObject toGeneratedObject(ModuleMXBeanEntry mbe, Optional<String> copyright) {
43 FullyQualifiedName abstractFQN = new FullyQualifiedName(mbe.getPackageName(), mbe.getAbstractModuleName());
44 Optional<String> classJavaDoc = Optional.fromNullable(mbe.getNullableDescription());
45 AbstractModuleTemplate abstractModuleTemplate = TemplateFactory.abstractModuleTemplateFromMbe(mbe);
46 Optional<String> header = abstractModuleTemplate.getHeaderString();
48 List<FullyQualifiedName> implementedInterfaces = new ArrayList<>();
49 for(String implemented: abstractModuleTemplate.getTypeDeclaration().getImplemented()) {
50 implementedInterfaces.add(FullyQualifiedName.fromString(implemented));
52 Optional<FullyQualifiedName> maybeRegistratorType;
53 if (abstractModuleTemplate.isRuntime()) {
54 maybeRegistratorType = Optional.of(FullyQualifiedName.fromString(abstractModuleTemplate.getRegistratorType()));
56 maybeRegistratorType = Optional.absent();
59 return toGeneratedObject(abstractFQN, copyright, header, classJavaDoc, implementedInterfaces,
60 abstractModuleTemplate.getModuleFields(), maybeRegistratorType, abstractModuleTemplate.getMethods(),
61 mbe.getYangModuleQName());
64 public GeneratedObject toGeneratedObject(FullyQualifiedName abstractFQN,
65 Optional<String> copyright,
66 Optional<String> header,
67 Optional<String> classJavaDoc,
68 List<FullyQualifiedName> implementedInterfaces,
69 List<ModuleField> moduleFields,
70 Optional<FullyQualifiedName> maybeRegistratorType,
71 List<? extends Method> methods,
72 QName yangModuleQName) {
73 JavaFileInputBuilder b = new JavaFileInputBuilder();
75 Annotation moduleQNameAnnotation = Annotation.createModuleQNameANnotation(yangModuleQName);
76 b.addClassAnnotation(moduleQNameAnnotation);
78 b.setFqn(abstractFQN);
79 b.setTypeName(TypeName.absClassType);
81 b.setCopyright(copyright);
83 b.setClassJavaDoc(classJavaDoc);
84 for(FullyQualifiedName implemented: implementedInterfaces) {
85 b.addImplementsFQN(implemented);
87 if (classJavaDoc.isPresent()) {
88 b.addClassAnnotation(format("@%s(value=\"%s\")", Description.class.getCanonicalName(), classJavaDoc.get()));
92 b.addToBody(getLogger(abstractFQN));
94 b.addToBody("//attributes start");
95 for(ModuleField moduleField: moduleFields) {
96 b.addToBody(moduleField.toString() +"\n");
99 b.addToBody("//attributes end");
102 b.addToBody(getCommonFields(abstractFQN));
105 b.addToBody(getNewConstructor(abstractFQN));
106 b.addToBody(getCopyFromOldConstructor(abstractFQN));
108 b.addToBody(getRuntimeRegistratorCode(maybeRegistratorType));
109 b.addToBody(getValidationMethods(moduleFields));
111 b.addToBody(getCachesOfResolvedDependencies(moduleFields));
112 b.addToBody(getCachesOfResolvedIdentityRefs(moduleFields));
113 b.addToBody(getGetInstance(moduleFields));
114 b.addToBody(getReuseLogic(moduleFields, abstractFQN));
115 b.addToBody(getEqualsAndHashCode(abstractFQN));
117 b.addToBody(getMethods(methods));
119 return new GeneratedObjectBuilder(b.build()).toGeneratedObject();
122 private static String getMethods(List<? extends Method> methods) {
123 String result = "\n// getters and setters\n";
124 for(Method method: methods) {
125 result += method.toString()+"\n";
130 private static String getEqualsAndHashCode(FullyQualifiedName abstractFQN) {
133 "public boolean equals(Object o) {\n"+
134 "if (this == o) return true;\n"+
135 "if (o == null || getClass() != o.getClass()) return false;\n"+
136 format("%s that = (%1$s) o;\n", abstractFQN.getTypeName())+
137 "return identifier.equals(that.identifier);\n"+
141 "public int hashCode() {\n"+
142 "return identifier.hashCode();\n"+
146 private static String getReuseLogic(List<ModuleField> moduleFields, FullyQualifiedName abstractFQN) {
147 String result = "\n"+
148 format("public boolean canReuseInstance(%s oldModule){\n", abstractFQN.getTypeName())+
149 "// allow reusing of old instance if no parameters was changed\n"+
150 "return isSame(oldModule);\n"+
153 format("public %s reuseInstance(%1$s oldInstance){\n", AutoCloseable.class.getCanonicalName())+
154 "// implement if instance reuse should be supported. Override canReuseInstance to change the criteria.\n"+
155 "return oldInstance;\n"+
157 // isSame method that detects changed fields
159 format("public boolean isSame(%s other) {\n", abstractFQN.getTypeName())+
160 "if (other == null) {\n"+
161 "throw new IllegalArgumentException(\"Parameter 'other' is null\");\n"+
163 // loop through fields, do deep equals on each field
165 for (ModuleField moduleField : moduleFields) {
166 if (moduleField.isListOfDependencies()) {
168 "if (%1$sDependency.equals(other.%1$sDependency) == false) {\n"+
171 "for (int idx = 0; idx < %1$sDependency.size(); idx++) {\n"+
172 "if (%1$sDependency.get(idx) != other.%1$sDependency.get(idx)) {\n"+
175 "}\n" ,moduleField.getName());
176 } else if (moduleField.isDependent()) {
178 "if (%sDependency != other.%1$sDependency) { // reference to dependency must be same\n"+
180 "}\n",moduleField.getName());
183 "if (java.util.Objects.deepEquals(%s, other.%1$s) == false) {\n"+
185 "}\n", moduleField.getName());
195 private static String getGetInstance(List<ModuleField> moduleFields) {
196 String result = "\n"+
198 format("public final %s getInstance() {\n", AutoCloseable.class.getCanonicalName())+
199 "if(instance==null) {\n";
200 // create instance start
202 // loop through dependent fields, use dependency resolver to instantiate dependencies. Do it in loop in case field represents list of dependencies.
203 Map<ModuleField, String> resolveDependenciesMap = new HashMap<>();
204 for(ModuleField moduleField: moduleFields) {
205 if (moduleField.isDependent()) {
207 String osgi = moduleField.getDependency().getSie().getExportedOsgiClassName();
208 if (moduleField.isList()) {
210 "%sDependency = new java.util.ArrayList<%s>();\n"+
211 "for(javax.management.ObjectName dep : %1$s) {\n"+
212 "%1$sDependency.add(dependencyResolver.resolveInstance(%2$s.class, dep, %1$sJmxAttribute));\n"+
213 "}\n", moduleField.getName(), osgi);
216 "%1$sDependency = dependencyResolver.resolveInstance(%2$s.class, %1$s, %1$sJmxAttribute);\n",
217 moduleField.getName(), osgi);
219 resolveDependenciesMap.put(moduleField, str);
223 // wrap each field resolvation statement with if !=null when dependency is not mandatory
224 for (Map.Entry<ModuleField, String> entry : resolveDependenciesMap.entrySet()) {
225 if (entry.getKey().getDependency().isMandatory() == false) {
226 checkState(entry.getValue().endsWith(";\n"));
227 result += format("if (%s!=null) {\n%s}\n", entry.getKey().getName(), entry.getValue());
229 result += entry.getValue();
233 // add code to inject dependency resolver to fields that support it
234 for(ModuleField moduleField: moduleFields) {
235 if (moduleField.isNeedsDepResolver()) {
236 result += format("if (%s!=null){\n", moduleField.getName());
237 if (moduleField.isList()) {
239 "for(%s candidate : %s) {\n"+
240 "candidate.injectDependencyResolver(dependencyResolver);\n"+
241 "}\n", moduleField.getGenericInnerType(), moduleField.getName());
243 result += format("%s.injectDependencyResolver(dependencyResolver);\n", moduleField.getName());
249 // identity refs need to be injected with dependencyResolver and base class
250 for (ModuleField moduleField : moduleFields) {
251 if (moduleField.isIdentityRef()) {
252 result += format("if (%s!=null) {", moduleField.getName());
253 result += format("set%s(%s.resolveIdentity(dependencyResolver, %s.class));",
254 moduleField.getAttributeName(), moduleField.getName(),
255 ((IdentityRefModuleField)moduleField).getIdentityBaseClass());
260 // create instance end: reuse and recreate logic
261 result += "if(oldInstance!=null && canReuseInstance(oldModule)) {\n"+
262 "instance = reuseInstance(oldInstance);\n"+
264 "if(oldInstance!=null) {\n"+
266 "oldInstance.close();\n"+
267 "} catch(Exception e) {\n"+
268 "logger.error(\"An error occurred while closing old instance \" + oldInstance, e);\n"+
271 "instance = createInstance();\n"+
272 "if (instance == null) {\n"+
273 "throw new IllegalStateException(\"Error in createInstance - null is not allowed as return value\");\n"+
277 "return instance;\n"+
279 format("public abstract %s createInstance();\n", AutoCloseable.class.getCanonicalName());
284 private static String getCommonFields(FullyQualifiedName abstractFQN) {
286 format("private final %s oldModule;\n", abstractFQN.getTypeName())+
287 format("private final %s oldInstance;\n", AutoCloseable.class.getCanonicalName())+
288 format("private %s instance;\n", AutoCloseable.class.getCanonicalName())+
289 format("private final %s dependencyResolver;\n", DependencyResolver.class.getCanonicalName())+
290 format("private final %s identifier;\n", ModuleIdentifier.class.getCanonicalName())+
292 format("public %s getIdentifier() {\n", ModuleIdentifier.class.getCanonicalName())+
293 "return identifier;\n"+
297 private static String getCachesOfResolvedIdentityRefs(List<ModuleField> moduleFields) {
298 StringBuilder result = new StringBuilder();
299 for (ModuleField moduleField : moduleFields) {
300 if (moduleField.isIdentityRef()) {
301 IdentityRefModuleField field = (IdentityRefModuleField) moduleField;
302 result.append(format("private %s %s;\n", field.getIdentityClassType(), field.getIdentityClassName()));
305 return result.toString();
308 private static String getCachesOfResolvedDependencies(List<ModuleField> moduleFields) {
309 StringBuilder result = new StringBuilder();
310 for (ModuleField moduleField: moduleFields) {
311 if (moduleField.isDependent()) {
312 String osgi = moduleField.getDependency().getSie().getExportedOsgiClassName();
313 if (moduleField.isList()) {
315 .append(format("private java.util.List<%s> %sDependency = new java.util.ArrayList<%s>();", osgi, moduleField.getName(), osgi))
316 .append(format("protected final java.util.List<%s> get%sDependency(){\n", osgi, moduleField.getAttributeName()))
317 .append(format("return %sDependency;\n", moduleField.getName()))
320 result.append(format(
321 "private %s %sDependency;\n"+
322 "protected final %s get%sDependency(){\n"+
323 "return %sDependency;\n"+
325 osgi, moduleField.getName(), osgi, moduleField.getAttributeName(), moduleField.getName()));
329 return result.toString();
332 private static String getRuntimeRegistratorCode(Optional<FullyQualifiedName> maybeRegistratorType) {
333 if (maybeRegistratorType.isPresent()) {
334 String registratorType = maybeRegistratorType.get().toString();
337 format("private %s rootRuntimeBeanRegistratorWrapper;\n", registratorType)+
339 format("public %s getRootRuntimeBeanRegistratorWrapper(){\n", registratorType)+
340 "return rootRuntimeBeanRegistratorWrapper;\n"+
344 format("public void setRuntimeBeanRegistrator(%s rootRuntimeRegistrator){\n", RootRuntimeBeanRegistrator.class.getCanonicalName())+
345 format("this.rootRuntimeBeanRegistratorWrapper = new %s(rootRuntimeRegistrator);\n", registratorType)+
352 private static String getValidationMethods(List<ModuleField> moduleFields) {
353 String result = "\n"+
355 "public void validate() {\n";
356 // validate each mandatory dependency
357 for(ModuleField moduleField: moduleFields) {
358 if (moduleField.isDependent() && moduleField.getDependency().isMandatory()) {
359 if (moduleField.isList()) {
361 format("for(javax.management.ObjectName dep : %s) {\n", moduleField.getName()) +
362 format(" dependencyResolver.validateDependency(%s.class, dep, %sJmxAttribute);\n",
363 moduleField.getDependency().getSie().getFullyQualifiedName(), moduleField.getName()) +
366 result += format("dependencyResolver.validateDependency(%s.class, %s, %sJmxAttribute);",
367 moduleField.getDependency().getSie().getFullyQualifiedName(), moduleField.getName(), moduleField.getName());
372 "customValidation();\n"+
375 "protected void customValidation() {\n"+
380 private static String getLogger(FullyQualifiedName fqn) {
381 return format("private static final %s logger = %s.getLogger(%s.class);",
382 Logger.class.getCanonicalName(), LoggerFactory.class.getCanonicalName(), fqn);
385 // assumes that each parameter name corresponds to an field in this class, constructs lines setting this.field = field;
386 private static String getConstructorStart(FullyQualifiedName fqn,
387 LinkedHashMap<String, String> parameters, String after) {
388 String paramString = Joiner.on(",").withKeyValueSeparator(" ").join(parameters);
390 for (String paramName : parameters.values()) {
391 setters += format("this.%s = %1$s;\n", paramName);
393 return format("public %s(", fqn.getTypeName()) +
401 private static String getNewConstructor(FullyQualifiedName abstractFQN) {
402 LinkedHashMap<String, String> parameters = new LinkedHashMap<>();
403 parameters.put(ModuleIdentifier.class.getCanonicalName(), "identifier");
404 parameters.put(DependencyResolver.class.getCanonicalName(), "dependencyResolver");
406 String setToNulls = "this.oldInstance=null;\n" +
407 "this.oldModule=null;\n";
408 return getConstructorStart(abstractFQN, parameters, setToNulls);
411 private static String getCopyFromOldConstructor(FullyQualifiedName abstractFQN) {
412 LinkedHashMap<String, String> parameters = new LinkedHashMap<>();
413 parameters.put(ModuleIdentifier.class.getCanonicalName(), "identifier");
414 parameters.put(DependencyResolver.class.getCanonicalName(), "dependencyResolver");
415 parameters.put(abstractFQN.getTypeName(), "oldModule");
416 parameters.put(AutoCloseable.class.getCanonicalName(), "oldInstance");
417 return getConstructorStart(abstractFQN, parameters, "");