/* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static java.lang.String.format; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.common.collect.Lists; import java.io.Closeable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import javax.lang.model.element.Modifier; import org.opendaylight.controller.config.api.runtime.HierarchicalRuntimeBeanRegistration; import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator; import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry; import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation; import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field; import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition; import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper; public class RuntimeRegistratorFtlTemplate extends GeneralClassTemplate { private RuntimeRegistratorFtlTemplate(RuntimeBeanEntry runtimeBeanEntry, String name, List fields, List methods) { // TODO header super(null, runtimeBeanEntry.getPackageName(), name, Collections .emptyList(), Collections.singletonList(Closeable.class .getCanonicalName()), fields, methods); } public static RuntimeBeanEntry findRoot( Collection runtimeBeanEntries) { RuntimeBeanEntry result = null; for (RuntimeBeanEntry rb : runtimeBeanEntries) { if (rb.isRoot()) { if (result != null) { throw new IllegalArgumentException( "More than one root runtime bean found"); } result = rb; } } if (result != null) { return result; } throw new IllegalArgumentException("No root runtime bean found"); } private static String constructConstructorBody( List constructorParameters) { StringBuilder constructorBody = new StringBuilder(); for (Field field : constructorParameters) { constructorBody.append("this."); constructorBody.append(field.getName()); constructorBody.append("="); constructorBody.append(field.getName()); constructorBody.append(";\n"); } return constructorBody.toString(); } // TODO Move to factory /** * Get registrator and n registration ftls where n is equal to total number * of runtime beans in hierarchy. */ public static Map create(RuntimeBeanEntry rootRB) { checkArgument(rootRB.isRoot(), "RuntimeBeanEntry must be root"); String registratorName = getJavaNameOfRuntimeRegistrator(rootRB); List methods = new ArrayList<>(); Field rootRuntimeBeanRegistratorField = new Field( Collections.singletonList(Modifier.FINAL), RootRuntimeBeanRegistrator.class.getName(), "rootRuntimeBeanRegistrator"); List constructorParameters = Lists .newArrayList(rootRuntimeBeanRegistratorField); String constructorBody = constructConstructorBody(constructorParameters); MethodDefinition constructor = MethodDefinition.createConstructor( registratorName, constructorParameters, constructorBody); methods.add(constructor); LinkedHashMap RuntimeRegistratorFtlTemplates = createRegistrationHierarchy(rootRB, Collections.emptySet()); RuntimeRegistratorFtlTemplate rootFtlFile = RuntimeRegistratorFtlTemplates .values().iterator().next(); {// add register(rootruntimemxbean) String fullyQualifiedNameOfMXBean = FullyQualifiedNameHelper .getFullyQualifiedName(rootRB.getPackageName(), rootRB.getJavaNameOfRuntimeMXBean()); String childRegistratorFQN = rootFtlFile.getFullyQualifiedName(); Field rbParameter = new Field(fullyQualifiedNameOfMXBean, "rb"); StringBuilder registerBody = new StringBuilder(); registerBody.append(format("%s %s = this.%s.registerRoot(%s);\n", HierarchicalRuntimeBeanRegistration.class .getCanonicalName(), hierachicalRegistration .getName(), rootRuntimeBeanRegistratorField .getName(), rbParameter.getName())); registerBody.append(format("return new %s(%s);\n", rootFtlFile.getFullyQualifiedName(), hierachicalRegistration.getName())); MethodDefinition registerMethod = new MethodDefinition( childRegistratorFQN, "register", Collections.singletonList(rbParameter), registerBody.toString()); methods.add(registerMethod); } MethodDefinition closeRegistrator = createCloseMethodToCloseField(rootRuntimeBeanRegistratorField); methods.add(closeRegistrator); // TODO add header GeneralClassTemplate registrator = new GeneralClassTemplate(null, rootRB.getPackageName(), registratorName, Collections.emptyList(), Collections.singletonList(Closeable.class .getCanonicalName()), constructorParameters, methods); checkState(!RuntimeRegistratorFtlTemplates.containsKey(registrator .getTypeDeclaration().getName()), "Name conflict: " + registrator.getTypeDeclaration().getName()); Map result = new HashMap<>(); result.putAll(RuntimeRegistratorFtlTemplates); result.put(registrator.getTypeDeclaration().getName(), registrator); return result; } private static Field hierachicalRegistration = new Field( Collections.singletonList(Modifier.FINAL), HierarchicalRuntimeBeanRegistration.class.getCanonicalName(), "registration"); // TODO move to factory + RuntimeBeanEntry /** * Create ftls representing registrations. First registration is represents * parent. * * @return map containing java class name as key, instance representing the * java file as value. */ private static LinkedHashMap createRegistrationHierarchy( RuntimeBeanEntry parent, Set occupiedKeys) { LinkedHashMap unorderedResult = new LinkedHashMap<>(); List methods = new ArrayList<>(); // hierarchy of ON is created as follows: // root RB: , type=RuntimeBean // 1st RB in hierarchy: , type=RuntimeBean, : key or counter // n-th RB in hierarchy has same ON as n-1, with added : key or counter if (occupiedKeys.contains(parent.getJavaNamePrefix())) { throw new IllegalArgumentException( "Name conflict in runtime bean hierarchy - java name found more than " + "once. Consider using java-name extension. Conflicting name: " + parent.getJavaNamePrefix()); } Set currentOccupiedKeys = new HashSet<>(occupiedKeys); currentOccupiedKeys.add(parent.getJavaNamePrefix()); Field registratorsMapField = new Field(Collections.singletonList(Modifier.FINAL), TypeHelper.getGenericType(Map.class, String.class, AtomicInteger.class), "unkeyedMap", "new " + TypeHelper.getGenericType(HashMap.class, String.class, AtomicInteger.class) + "()"); // create register methods for children for (RuntimeBeanEntry child : parent.getChildren()) { checkArgument(parent.getPackageName() .equals(child.getPackageName()), "Invalid package name"); // call itself recursively to generate child // registrators/registrations LinkedHashMap childRegistratorMap = createRegistrationHierarchy( child, currentOccupiedKeys); for (Entry entry : childRegistratorMap .entrySet()) { if (unorderedResult.containsKey(entry.getKey())) { throw new IllegalStateException( "Conflicting name found while generating runtime registration:" + entry.getKey()); } unorderedResult.put(entry.getKey(), entry.getValue()); } if (!childRegistratorMap.isEmpty()) { // first entry is the direct descendant according to the create // contract RuntimeRegistratorFtlTemplate childRegistrator = childRegistratorMap .values().iterator().next(); StringBuilder body = new StringBuilder(); String key, value; key = child.getJavaNamePrefix(); body.append(format( "String key = \"%s\"; //TODO: check for conflicts\n", key)); Optional childKeyJavaName = child.getKeyJavaName(); if (childKeyJavaName.isPresent()) { value = "bean.get" + childKeyJavaName.get() + "()"; value = "String.valueOf(" + value + ")"; } else { body.append("java.util.concurrent.atomic.AtomicInteger counter = unkeyedMap.get(key);\n" + "if (counter==null){\n" + "counter = new java.util.concurrent.atomic.AtomicInteger();\n" + "unkeyedMap.put(key, counter);\n" + "}\n"); value = "String.valueOf(counter.incrementAndGet())"; } body.append(format("String value = %s;\n", value)); body.append(format("%s r = %s.register(key, value, bean);\n", HierarchicalRuntimeBeanRegistration.class .getCanonicalName(), hierachicalRegistration .getName())); body.append(format("return new %s(r);", childRegistrator.getFullyQualifiedName())); Field param = new Field(Collections.singletonList(Modifier.FINAL), child.getJavaNameOfRuntimeMXBean(), "bean"); MethodDefinition register = new MethodDefinition( Collections.singletonList(Modifier.SYNCHRONIZED), childRegistrator.getFullyQualifiedName(), "register", Collections.singletonList(param), Collections.emptyList(), Collections.emptyList(), body.toString()); methods.add(register); } } // create parent registration String createdName = getJavaNameOfRuntimeRegistration(parent.getJavaNamePrefix()); List constructorParameters = Collections.singletonList(hierachicalRegistration); String constructorBody = constructConstructorBody(constructorParameters); MethodDefinition constructor = MethodDefinition.createConstructor( createdName, constructorParameters, constructorBody); MethodDefinition closeRegistrator = createCloseMethodToCloseField(hierachicalRegistration); methods.add(closeRegistrator); methods.add(constructor); List privateFields = Lists.newArrayList(registratorsMapField); privateFields.addAll(constructorParameters); RuntimeRegistratorFtlTemplate created = new RuntimeRegistratorFtlTemplate( parent, createdName, privateFields, methods); LinkedHashMap result = new LinkedHashMap<>(); result.put(created.getTypeDeclaration().getName(), created); checkState(!unorderedResult.containsKey(created.getTypeDeclaration() .getName()), "Naming conflict: " + created.getTypeDeclaration().getName()); result.putAll(unorderedResult); return result; } private static MethodDefinition createCloseMethodToCloseField(Field field) { String body = field.getName() + ".close();"; // TODO Thrown exception breaks build // return new MethodDefinition(Collections. emptyList(), "void", // "close", Collections. emptyList(), // Arrays.asList(IOException.class.getCanonicalName()), // Collections. emptyList(), body); List annotations = Lists.newArrayList(new Annotation( "Override", Collections.emptyList())); return new MethodDefinition(Collections.emptyList(), "void", "close", Collections.emptyList(), Collections.emptyList(), annotations, body); } @VisibleForTesting public static String getJavaNameOfRuntimeRegistration(String javaNamePrefix) { return javaNamePrefix + "RuntimeRegistration"; } public static String getJavaNameOfRuntimeRegistrator(RuntimeBeanEntry rootRB) { checkArgument(rootRB.isRoot(), "RuntimeBeanEntry must be root"); return rootRB.getJavaNamePrefix() + "RuntimeRegistrator"; } }