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
9 import com.google.common.base.Optional
10 import org.opendaylight.controller.config.api.DependencyResolver
11 import org.opendaylight.controller.config.api.ModuleIdentifier
12 import org.opendaylight.controller.config.api.annotations.Description
13 import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator
14 import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry
15 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.AbstractModuleTemplate
16 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory
17 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation
18 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.IdentityRefModuleField
19 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Method
20 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField
21 import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.*
22 import org.opendaylight.yangtools.yang.common.QName
23 import org.slf4j.Logger
24 import org.slf4j.LoggerFactory
26 public class AbsModuleGeneratedObjectFactory {
28 public GeneratedObject toGeneratedObject(ModuleMXBeanEntry mbe, Optional<String> copyright) {
29 FullyQualifiedName abstractFQN = new FullyQualifiedName(mbe.getPackageName(), mbe.getAbstractModuleName())
30 Optional<String> classJavaDoc = Optional.fromNullable(mbe.getNullableDescription())
31 AbstractModuleTemplate abstractModuleTemplate = TemplateFactory.abstractModuleTemplateFromMbe(mbe)
32 Optional<String> header = abstractModuleTemplate.headerString;
33 List<FullyQualifiedName> implementedInterfaces = abstractModuleTemplate.getTypeDeclaration().getImplemented().collect {
34 FullyQualifiedName.fromString(it)
36 Optional<FullyQualifiedName> maybeRegistratorType
37 if (abstractModuleTemplate.isRuntime()) {
38 maybeRegistratorType = Optional.of(FullyQualifiedName.fromString(abstractModuleTemplate.getRegistratorType()))
40 maybeRegistratorType = Optional.absent()
43 return toGeneratedObject(abstractFQN, copyright, header, classJavaDoc, implementedInterfaces,
44 abstractModuleTemplate.getModuleFields(), maybeRegistratorType, abstractModuleTemplate.getMethods(),
49 public GeneratedObject toGeneratedObject(FullyQualifiedName abstractFQN,
50 Optional<String> copyright,
51 Optional<String> header,
52 Optional<String> classJavaDoc,
53 List<FullyQualifiedName> implementedInterfaces,
54 List<ModuleField> moduleFields,
55 Optional<FullyQualifiedName> maybeRegistratorType,
57 QName yangModuleQName) {
58 JavaFileInputBuilder b = new JavaFileInputBuilder()
60 Annotation moduleQNameAnnotation = Annotation.createModuleQNameANnotation(yangModuleQName)
61 b.addClassAnnotation(moduleQNameAnnotation)
64 b.setTypeName(TypeName.absClassType)
66 b.setCopyright(copyright);
68 b.setClassJavaDoc(classJavaDoc);
69 implementedInterfaces.each { b.addImplementsFQN(it) }
70 if (classJavaDoc.isPresent()) {
71 b.addClassAnnotation("@${Description.canonicalName}(value=\"${classJavaDoc.get()}\")")
75 b.addToBody(getLogger(abstractFQN));
77 b.addToBody("//attributes start");
79 b.addToBody(moduleFields.collect { it.toString() }.join("\n"))
81 b.addToBody("//attributes end");
84 b.addToBody(getCommonFields(abstractFQN));
87 b.addToBody(getNewConstructor(abstractFQN))
88 b.addToBody(getCopyFromOldConstructor(abstractFQN))
90 b.addToBody(getRuntimeRegistratorCode(maybeRegistratorType))
91 b.addToBody(getValidationMethods(moduleFields))
93 b.addToBody(getCachesOfResolvedDependencies(moduleFields))
94 b.addToBody(getCachesOfResolvedIdentityRefs(moduleFields))
95 b.addToBody(getGetInstance(moduleFields))
96 b.addToBody(getReuseLogic(moduleFields, abstractFQN))
97 b.addToBody(getEqualsAndHashCode(abstractFQN))
99 b.addToBody(getMethods(methods))
101 return new GeneratedObjectBuilder(b.build()).toGeneratedObject()
104 private static String getMethods(List<Method> methods) {
106 // getters and setters
108 result += methods.collect{it.toString()}.join("\n")
112 private static String getEqualsAndHashCode(FullyQualifiedName abstractFQN) {
115 public boolean equals(Object o) {
116 if (this == o) return true;
117 if (o == null || getClass() != o.getClass()) return false;
118 ${abstractFQN.typeName} that = (${abstractFQN.typeName}) o;
119 return identifier.equals(that.identifier);
123 public int hashCode() {
124 return identifier.hashCode();
129 private static String getReuseLogic(List<ModuleField> moduleFields, FullyQualifiedName abstractFQN) {
131 public boolean canReuseInstance(${abstractFQN.typeName} oldModule){
132 // allow reusing of old instance if no parameters was changed
133 return isSame(oldModule);
136 public ${AutoCloseable.canonicalName} reuseInstance(${AutoCloseable.canonicalName} oldInstance){
137 // implement if instance reuse should be supported. Override canReuseInstance to change the criteria.
141 // isSame method that detects changed fields
143 public boolean isSame(${abstractFQN.typeName} other) {
145 throw new IllegalArgumentException("Parameter 'other' is null");
148 // loop through fields, do deep equals on each field
149 result += moduleFields.collect { field ->
150 if (field.isListOfDependencies()) {
152 if (${field.name}Dependency.equals(other.${field.name}Dependency) == false) {
155 for (int idx = 0; idx < ${field.name}Dependency.size(); idx++) {
156 if (${field.name}Dependency.get(idx) != other.${field.name}Dependency.get(idx)) {
161 } else if (field.isDependent()) {
163 if (${field.name}Dependency != other.${field.name}Dependency) { // reference to dependency must be same
169 if (java.util.Objects.deepEquals(${field.name}, other.${field.name}) == false) {
185 private static String getGetInstance(List<ModuleField> moduleFields) {
188 public final ${AutoCloseable.canonicalName} getInstance() {
191 // create instance start
193 // loop through dependent fields, use dependency resolver to instantiate dependencies. Do it in loop in case field represents list of dependencies.
194 Map<ModuleField, String> resolveDependenciesMap = moduleFields.findAll {
196 }.collectEntries { ModuleField field ->
197 [field, field.isList() ?
199 ${field.name}Dependency = new java.util.ArrayList<${field.dependency.sie.exportedOsgiClassName}>();
200 for(javax.management.ObjectName dep : ${field.name}) {
201 ${field.name}Dependency.add(dependencyResolver.resolveInstance(${
202 field.dependency.sie.exportedOsgiClassName
203 }.class, dep, ${field.name}JmxAttribute));
208 ${field.name}Dependency = dependencyResolver.resolveInstance(${
209 field.dependency.sie.exportedOsgiClassName
210 }.class, ${field.name}, ${field.name}JmxAttribute);
214 // wrap each field resolvation statement with if !=null when dependency is not mandatory
215 def wrapWithNullCheckClosure = {Map<ModuleField, String> map, predicate -> map.collect { ModuleField key, String value ->
217 if(${key.name}!=null) {
224 result += wrapWithNullCheckClosure(resolveDependenciesMap, {ModuleField key ->
225 key.getDependency().isMandatory() == false} )
227 // add code to inject dependency resolver to fields that support it
228 Map<ModuleField, String> injectDepsMap = moduleFields.findAll { it.needsDepResolver }.collectEntries { field ->
229 if (field.isList()) {
231 for(${field.genericInnerType} candidate : ${field.name}) {
232 candidate.injectDependencyResolver(dependencyResolver);
236 return [field, "${field.name}.injectDependencyResolver(dependencyResolver);"]
240 result += wrapWithNullCheckClosure(injectDepsMap, {true})
242 // identity refs need to be injected with dependencyResolver and base class
243 Map<ModuleField, String> resolveIdentityMap = moduleFields.findAll { it.isIdentityRef() }.collectEntries { IdentityRefModuleField field ->
245 "set${field.attributeName}(${field.name}.resolveIdentity(dependencyResolver, ${field.identityBaseClass}.class));"]
248 result += wrapWithNullCheckClosure(resolveIdentityMap, {true})
250 // create instance end: reuse and recreate logic
252 if(oldInstance!=null && canReuseInstance(oldModule)) {
253 instance = reuseInstance(oldInstance);
255 if(oldInstance!=null) {
258 } catch(Exception e) {
259 logger.error("An error occurred while closing old instance " + oldInstance, e);
262 instance = createInstance();
263 if (instance == null) {
264 throw new IllegalStateException("Error in createInstance - null is not allowed as return value");
270 public abstract ${AutoCloseable.canonicalName} createInstance();
275 private static String getCommonFields(FullyQualifiedName abstractFQN) {
277 private final ${abstractFQN.typeName} oldModule;
278 private final ${AutoCloseable.canonicalName} oldInstance;
279 private ${AutoCloseable.canonicalName} instance;
280 private final ${DependencyResolver.canonicalName} dependencyResolver;
281 private final ${ModuleIdentifier.canonicalName} identifier;
283 public ${ModuleIdentifier.canonicalName} getIdentifier() {
289 private static String getCachesOfResolvedIdentityRefs(List<ModuleField> moduleFields) {
290 return moduleFields.findAll { it.isIdentityRef() }.collect { IdentityRefModuleField field ->
291 "private ${field.identityClassType} ${field.identityClassName};"
295 private static String getCachesOfResolvedDependencies(List<ModuleField> moduleFields) {
296 return moduleFields.findAll { it.dependent }.collect { field ->
297 if (field.isList()) {
299 private java.util.List<${field.dependency.sie.exportedOsgiClassName}> ${
301 }Dependency = new java.util.ArrayList<${field.dependency.sie.exportedOsgiClassName}>();
302 protected final java.util.List<${field.dependency.sie.exportedOsgiClassName}> get${
305 return ${field.name}Dependency;
310 private ${field.dependency.sie.exportedOsgiClassName} ${field.name}Dependency;
311 protected final ${field.dependency.sie.exportedOsgiClassName} get${field.attributeName}Dependency(){
312 return ${field.name}Dependency;
319 private static String getRuntimeRegistratorCode(Optional<FullyQualifiedName> maybeRegistratorType) {
320 if (maybeRegistratorType.isPresent()) {
321 String registratorType = maybeRegistratorType.get()
324 private ${registratorType} rootRuntimeBeanRegistratorWrapper;
326 public ${registratorType} getRootRuntimeBeanRegistratorWrapper(){
327 return rootRuntimeBeanRegistratorWrapper;
331 public void setRuntimeBeanRegistrator(${RootRuntimeBeanRegistrator.canonicalName} rootRuntimeRegistrator){
332 this.rootRuntimeBeanRegistratorWrapper = new ${registratorType}(rootRuntimeRegistrator);
340 private static String getValidationMethods(List<ModuleField> moduleFields) {
343 public void validate() {
345 // validate each mandatory dependency
346 List<String> lines = moduleFields.findAll{(it.dependent && it.dependency.mandatory)}.collect { field ->
347 if (field.isList()) {
349 "for(javax.management.ObjectName dep : ${field.name}) {\n" +
350 " dependencyResolver.validateDependency(${field.dependency.sie.fullyQualifiedName}.class, dep, ${field.name}JmxAttribute);\n" +
353 return "dependencyResolver.validateDependency(${field.dependency.sie.fullyQualifiedName}.class, ${field.name}, ${field.name}JmxAttribute);"
356 result += lines.findAll { it.isEmpty() == false }.join("\n")
361 protected void customValidation(){
367 private static String getLogger(FullyQualifiedName fqn) {
368 return "private static final ${Logger.canonicalName} logger = ${LoggerFactory.canonicalName}.getLogger(${fqn.toString()}.class);"
371 // assumes that each parameter name corresponds to an field in this class, constructs lines setting this.field = field;
372 private static String getConstructorStart(FullyQualifiedName fqn,
373 LinkedHashMap<String, String> parameters, String after) {
374 return "public ${fqn.typeName}(" +
375 parameters.collect { it.key + " " + it.value }.join(",") +
377 parameters.values().collect { "this.${it}=${it};\n" }.join() +
382 private static String getNewConstructor(FullyQualifiedName abstractFQN) {
383 LinkedHashMap<String, String> parameters = [
384 (ModuleIdentifier.canonicalName): "identifier",
385 (DependencyResolver.canonicalName): "dependencyResolver"
387 String setToNulls = ["oldInstance", "oldModule"].collect { "this.${it}=null;\n" }.join()
388 return getConstructorStart(abstractFQN, parameters, setToNulls)
391 private static String getCopyFromOldConstructor(FullyQualifiedName abstractFQN) {
392 LinkedHashMap<String, String> parameters = [
393 (ModuleIdentifier.canonicalName): "identifier",
394 (DependencyResolver.canonicalName): "dependencyResolver",
395 (abstractFQN.typeName): "oldModule",
396 (AutoCloseable.canonicalName): "oldInstance"
398 return getConstructorStart(abstractFQN, parameters, "")