aboutsummaryrefslogtreecommitdiffstats
path: root/appc-client/code-generator/src/main
diff options
context:
space:
mode:
authorJoey Sullivan <joey.sullivan@amdocs.com>2017-12-22 15:32:48 +0000
committerSkip Wonnell <skip@att.com>2017-12-22 17:35:43 +0000
commitf2614e766147fd345058f137852eeaf892181fc1 (patch)
treec67278f382f95332dcf509d07e3c69976952ef59 /appc-client/code-generator/src/main
parent9475355553b59df58d6f516e2c9109babae9ffd0 (diff)
Automate Updates to the API Client Library
Removed client-kit generated code from git repo. This code is now generated under the target folder. client-kit now uses latest appc-provider-lcm.yang on the branch. Change-Id: I7a20774b60bb447343886997c32ca3b3c5dcbafa Issue-ID: APPC-360 Signed-off-by: Joey Sullivan <joey.sullivan@amdocs.com>
Diffstat (limited to 'appc-client/code-generator/src/main')
-rw-r--r--appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/CLI.java4
-rw-r--r--appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/ContextBuilder.java4
-rw-r--r--appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/MavenPlugin.java86
-rw-r--r--appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/extensions/JsonContextBuilderImpl.java11
-rw-r--r--appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/extensions/YangContextBuilderImpl.java190
-rw-r--r--appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/impl/CodeGenWriter.java5
-rw-r--r--appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/impl/ModelGenerator.java9
-rw-r--r--appc-client/code-generator/src/main/resources/configuration/client-kit.properties2
-rw-r--r--appc-client/code-generator/src/main/resources/templates/open-api/yang-to-open-api.ftl9
9 files changed, 259 insertions, 61 deletions
diff --git a/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/CLI.java b/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/CLI.java
index e28b3706a..bcd68c8ab 100644
--- a/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/CLI.java
+++ b/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/CLI.java
@@ -26,6 +26,8 @@ package org.onap.appc.tools.generator.api;
import org.onap.appc.tools.generator.impl.ModelGenerator;
+import java.io.File;
+
public class CLI {
public static void main(String... args) throws Exception {
String sourceFile = args[0];
@@ -61,6 +63,6 @@ public class CLI {
+ " "+builderName
+ " <conf file>'");
ModelGenerator generator = new ModelGenerator();
- generator.execute(sourceFile, destinationFile, templateFile, builderName, contextConfName);
+ generator.execute((new File(sourceFile)).toURI().toURL(), destinationFile, templateFile, builderName, contextConfName);
}
}
diff --git a/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/ContextBuilder.java b/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/ContextBuilder.java
index a1826f270..e2831dec8 100644
--- a/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/ContextBuilder.java
+++ b/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/ContextBuilder.java
@@ -24,11 +24,11 @@
package org.onap.appc.tools.generator.api;
-import java.io.FileNotFoundException;
import java.io.IOException;
+import java.net.URL;
import java.util.Map;
public interface ContextBuilder {
- Map<String, Object> buildContext(String sourceFile, String contextConf) throws IOException;
+ Map<String, Object> buildContext(URL sourceURL, String contextConf) throws IOException;
}
diff --git a/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/MavenPlugin.java b/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/MavenPlugin.java
index 9e160eed2..d26170774 100644
--- a/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/MavenPlugin.java
+++ b/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/api/MavenPlugin.java
@@ -24,7 +24,6 @@
package org.onap.appc.tools.generator.api;
-import org.onap.appc.tools.generator.impl.ModelGenerator;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
@@ -32,12 +31,17 @@ import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
+import org.onap.appc.tools.generator.impl.ModelGenerator;
+import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
import java.nio.file.Paths;
@Mojo(
- name = "generate",
+ name = "generate-sources",
defaultPhase = LifecyclePhase.GENERATE_SOURCES
)
public class MavenPlugin extends AbstractMojo {
@@ -65,20 +69,84 @@ public class MavenPlugin extends AbstractMojo {
ModelGenerator generator = new ModelGenerator();
try {
trace("\t === Called MavenPlugin on builder <" + contextBuilderClassName +">\n");
- generator.execute(sourceFileName,outputFileName,templateName,contextBuilderClassName,contextConfigFileName);
- String workDirectory = getWorkDirectory(outputFileName);
+
+ //the source file may be in the class path
+ //or on the file system
+ URL sourceFileURL = lookupURL(sourceFileName);
+
+ //prefix with the project absolute path to the output file
+ outputFileName = toAbsoluteFile(outputFileName);
+ String workDirectory = Paths.get(outputFileName).getParent().toString();
+ generator.execute(sourceFileURL,outputFileName,templateName,contextBuilderClassName,contextConfigFileName);
project.addCompileSourceRoot(workDirectory);
} catch (Exception e) {
- e.printStackTrace();
- throw new MojoExecutionException(e.getMessage());
+ throw new MojoExecutionException(e.getMessage(),e);
}
}
- private String getWorkDirectory(String outputFileName) throws IOException {
- String workDirPath = Paths.get(outputFileName.toString()).getParent().toString();
- return workDirPath;
+
+ /**
+ * Converts the file to absolute path. If the file does not exist prefix the maven project absolute path.
+ * @param filePath
+ * @return
+ */
+ private String toAbsoluteFile(String filePath){
+
+ File file = new File(filePath);
+
+ //if the file already exist just return the absolutePath
+ if(file.exists()){
+ return file.getAbsolutePath();
+ }
+
+
+ //prefix with the project absolute path to the output file
+ if(!file.isAbsolute()){
+ File projectDir = new File(this.project.getBuild().getDirectory()).getParentFile();
+ filePath = projectDir.getAbsolutePath() + "/" + filePath;
+ }
+
+ return filePath;
}
+ /**
+ * Tries three lookups
+ * First try to lookup the file in the classpath.
+ * else try relative path
+ * else try prefixing the relative path with the maven project path.
+
+ * @param filePath - A String denoting the source yang file path.
+ * @return URL - to the source yang file
+ * @throws MalformedURLException
+ */
+ private URL lookupURL(String filePath) throws IOException {
+ //check out the class path first
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ URL sourceYangURL = classLoader.getResource(filePath);
+
+ if (sourceYangURL != null) {
+ return sourceYangURL;
+ }
+
+ String errorMessage = String.format(
+ "YANG file <%s> not found in classpath or on the file system."
+ ,filePath
+ );
+
+ //check the file system first
+ File sourceFile = new File(toAbsoluteFile(filePath));
+ if (!sourceFile.exists()) {
+ throw new FileNotFoundException(errorMessage);
+ }
+ try {
+ sourceYangURL = sourceFile.toURI().toURL();
+ } catch (MalformedURLException e) {
+ throw new IOException(errorMessage,e);
+ }
+ return sourceYangURL;
+ }
+
+
private void trace(String message) {
getLog().info(message);
}
diff --git a/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/extensions/JsonContextBuilderImpl.java b/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/extensions/JsonContextBuilderImpl.java
index 6c408dd1d..34471ff4e 100644
--- a/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/extensions/JsonContextBuilderImpl.java
+++ b/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/extensions/JsonContextBuilderImpl.java
@@ -24,14 +24,14 @@
package org.onap.appc.tools.generator.extensions;
-import org.onap.appc.tools.generator.api.ContextBuilder;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onap.appc.tools.generator.api.ContextBuilder;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@@ -39,15 +39,18 @@ import java.util.Properties;
public class JsonContextBuilderImpl implements ContextBuilder {
@Override
- public Map<String, Object> buildContext(String sourceFile, String contextConf) throws IOException {
+ public Map<String, Object> buildContext(URL sourceURL, String contextConf) throws IOException {
//read json file
ObjectMapper mapper = new ObjectMapper();
- JsonNode model = mapper.readTree(new File(sourceFile));
+ JsonNode model = mapper.readTree(sourceURL);
//get context config file
Properties properties = new Properties();
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
InputStream inputStream = classloader.getResourceAsStream(contextConf);
+ if(inputStream == null){
+ throw new IOException(String.format("The file [%s] cannot be found in the class path",contextConf));
+ }
properties.load(inputStream);
//get context related properties
diff --git a/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/extensions/YangContextBuilderImpl.java b/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/extensions/YangContextBuilderImpl.java
index 67055c757..6307d1365 100644
--- a/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/extensions/YangContextBuilderImpl.java
+++ b/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/extensions/YangContextBuilderImpl.java
@@ -24,74 +24,190 @@
package org.onap.appc.tools.generator.extensions;
-import org.onap.appc.tools.generator.api.ContextBuilder;
import com.google.common.base.Optional;
-
+import org.onap.appc.tools.generator.api.ContextBuilder;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver;
-import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.ModuleEffectiveStatementImpl;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.InputStream;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
import java.net.URL;
-import java.util.Map;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
public class YangContextBuilderImpl implements ContextBuilder {
@Override
- public Map<String, Object> buildContext(String sourceFile, String contextConf) throws FileNotFoundException {
- try ( InputStream source = new FileInputStream(sourceFile) ) {}
- catch ( IOException ex) {
- throw new FileNotFoundException("YANG file <" + sourceFile + ">not found");
- }
+ public Map<String, Object> buildContext(URL sourceYangURL, String contextConf) throws IOException {
+
Optional<SchemaContext> sc = null;
try ( YangTextSchemaContextResolver yangContextResolver =
YangTextSchemaContextResolver.create("yang-context-resolver")) {
- yangContextResolver.registerSource(new URL("file:///" + sourceFile));
+ yangContextResolver.registerSource(sourceYangURL);
sc = yangContextResolver.getSchemaContext();
} catch (SchemaSourceException | IOException | YangSyntaxErrorException e) {
- e.printStackTrace();
+ throw new IOException(String.format("Exception occurred while processing sourceFileName %s ",sourceYangURL),e);
}
Map<String, Object> map = new HashMap<>();
if ( null != sc && sc.isPresent()) {
Set<Module> modules = sc.get().getModules();
- for (Module module : modules) {
- ModuleEffectiveStatementImpl impl = (ModuleEffectiveStatementImpl) module;
- map.put("module", module);
+ for (final Module module : modules) {
+
+ Module proxyModule = (new PackagePrivateReflectBug()).buildProxyObjects(module);
+ map.put("module", proxyModule);
}
}
return map;
}
- // @Override
- // public Map<String, Object> buildContext(String sourceFile, String
- // contextConf) throws FileNotFoundException {
- // InputStream source = new FileInputStream(sourceFile);
- // if (source == null) {
- // throw new FileNotFoundException("YANG file <" + sourceFile + ">not found");
- // }
- //
- // SchemaContext mSchema = parse(Collections.singletonList(source));
- //
- // Map<String, Object> map = new HashMap<>();
- // map.put("module", mSchema.getModules().iterator().next());
- // return map;
- // }
- //
- // private SchemaContext parse(List<InputStream> sources) {
- // YangParserImpl parser = new YangParserImpl();
- // Set<Module> modules = parser.parseYangModelsFromStreams(sources);
- // return parser.resolveSchemaContext(modules);
- // }
+ /**
+ * The Wrap Proxy uses java Proxy to work around bug JDK-8193172. The Issue is that if a super class is package
+ * private its public methods cause IllegalAccessException when using java reflect to access those methods from a
+ * subclass which is package public.
+ * <p>
+ * Example exception:
+ * Caused by: java.lang.IllegalAccessException: Class freemarker.ext.beans.BeansWrapper cannot access a
+ * member of class org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.AbstractEffectiveModule
+ * with modifiers "public final"
+ */
+ private static class PackagePrivateReflectBug {
+
+ /**
+ * Recursive method that traverses the yang model and wraps objects that extend package private classes
+ * with java proxy objects.
+ * @param candidateObj
+ * @param <T>
+ * @return
+ */
+ private <T> T buildProxyObjects(Object candidateObj) {
+
+ //can't proxy null so just return
+ if (candidateObj == null) {
+ return null;
+ }
+
+
+ Class<?> candidateClass = candidateObj.getClass();
+
+ //prevent a recursive proxy
+ if (Proxy.isProxyClass(candidateClass)) {
+ return (T) candidateObj;
+ }
+
+ //Can�t create a proxy an Array, so replace each
+ //element the array with a proxy if needed.
+ if (candidateClass.isArray()) {
+ Object[] sourceArray = (Object[]) candidateObj;
+ for (int i = 0; i < sourceArray.length; i++) {
+ sourceArray[i] = buildProxyObjects(sourceArray[i]);
+ }
+ return (T) candidateObj;
+ }
+
+ //Evaluate if this class must be wrapped in a proxy or not.
+ if (!isCandidateForProxy(candidateClass)) {
+ return (T) candidateObj;
+ }
+
+ //Collect the interfaces for the proxy. Proxy only work with classes
+ //that implement interfaces if there are none the obj cannot be wrapped
+ //with a proxy.
+ HashSet<Class<?>> interfaceSet = new HashSet<>();
+ collectInterfaces(candidateClass, interfaceSet);
+ if (interfaceSet.isEmpty()) {
+ return (T) candidateObj;
+ }
+ Class<?>[] interfaces = new Class<?>[interfaceSet.size()];
+ interfaceSet.toArray(interfaces);
+
+ //wrap the Object in a proxy
+ return (T) Proxy.newProxyInstance(
+ candidateClass.getClassLoader(),
+ interfaces,
+ (proxy, method, args) -> {
+ Object returnObj = method.invoke(candidateObj, args);
+ returnObj = buildProxyObjects(returnObj);
+ return returnObj;
+ }
+ );
+ }
+
+
+ /**
+ * This method determines if the specified class is a candidate for proxy.
+ *
+ * @param fromClass
+ * @return true - if the specifed class is a Candidate.
+ */
+ private boolean isCandidateForProxy(Class<?> fromClass) {
+
+ //a
+ Class<?>[] includeClasses = {
+ java.util.Collection.class,
+ java.util.Map.class,
+ org.opendaylight.yangtools.yang.model.api.Module.class
+
+ };
+
+ for (Class<?> toClass : includeClasses) {
+ if (toClass.isAssignableFrom(fromClass)) {
+ return true;
+ }
+ }
+
+ if (isExtendsPackagePrivateSuperClass(fromClass)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Test if any of the super packages are package private.
+ *
+ * @param clazz
+ * @return
+ */
+ private boolean isExtendsPackagePrivateSuperClass(Class<?> clazz) {
+ if (Object.class.equals(clazz)) {
+ return false;
+ }
+
+ int classModifiers = clazz.getModifiers();
+ if (Modifier.isPublic(classModifiers)) {
+ return isExtendsPackagePrivateSuperClass(clazz.getSuperclass());
+ }
+
+ return true;
+ }
+
+ /**
+ * Collect all of the interfaces this class can be cast too. Tavers the class hierarchy to include interfaces
+ * from the super classes.
+ *
+ * @param clazz
+ * @param classSet
+ */
+ private void collectInterfaces(Class<?> clazz, Set<Class<?>> classSet) {
+ Class<?>[] interfaces = clazz.getInterfaces();
+ classSet.addAll(Arrays.asList(interfaces));
+ Class<?> superClass = clazz.getSuperclass();
+
+ //
+ if (!Object.class.equals(superClass)) {
+ collectInterfaces(superClass, classSet);
+ }
+ }
+ }
}
diff --git a/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/impl/CodeGenWriter.java b/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/impl/CodeGenWriter.java
index 347c24c06..30ed2cabf 100644
--- a/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/impl/CodeGenWriter.java
+++ b/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/impl/CodeGenWriter.java
@@ -24,7 +24,10 @@
package org.onap.appc.tools.generator.impl;
-import java.io.*;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
diff --git a/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/impl/ModelGenerator.java b/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/impl/ModelGenerator.java
index 2275fdac7..e888aa804 100644
--- a/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/impl/ModelGenerator.java
+++ b/appc-client/code-generator/src/main/java/org/onap/appc/tools/generator/impl/ModelGenerator.java
@@ -24,13 +24,14 @@
package org.onap.appc.tools.generator.impl;
-import org.onap.appc.tools.generator.api.ContextBuilder;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
+import org.onap.appc.tools.generator.api.ContextBuilder;
import java.io.IOException;
import java.io.Writer;
+import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -38,10 +39,10 @@ import java.util.Map;
public class ModelGenerator {
- public void execute(String sourceFile, String destinationFile, String templateFile, String builderName, String contextConfName) throws IOException, ReflectiveOperationException {
+ public void execute(URL sourceFileURL, String destinationFile, String templateFile, String builderName, String contextConfName) throws IOException, ReflectiveOperationException {
ContextBuilder contextBuilder = (ContextBuilder) Class.forName(builderName).newInstance();
- Map<String, Object> context = contextBuilder.buildContext(sourceFile, contextConfName);
+ Map<String, Object> context = contextBuilder.buildContext(sourceFileURL, contextConfName);
Path destinationPath = Paths.get(destinationFile);
if (!Files.isDirectory(destinationPath))
@@ -68,4 +69,6 @@ public class ModelGenerator {
}
}
+
}
+
diff --git a/appc-client/code-generator/src/main/resources/configuration/client-kit.properties b/appc-client/code-generator/src/main/resources/configuration/client-kit.properties
index a0097c9fa..a2f73508a 100644
--- a/appc-client/code-generator/src/main/resources/configuration/client-kit.properties
+++ b/appc-client/code-generator/src/main/resources/configuration/client-kit.properties
@@ -28,7 +28,7 @@
#--------------------------------------------------------------------------------------------
ctx.model.package=org.onap.appc.client.lcm.model
ctx.api.package=org.onap.appc.client.lcm.api
-ctx.utils.package=org.onap.appc.client.lcm.utils
+ctx.utils.package=org.onap.appc
ctx.exceptions.package=org.onap.appc.client.lcm.exceptions
ctx.interface.classname=LifeCycleManagerStateful
diff --git a/appc-client/code-generator/src/main/resources/templates/open-api/yang-to-open-api.ftl b/appc-client/code-generator/src/main/resources/templates/open-api/yang-to-open-api.ftl
index 685efcb02..e92ee3b05 100644
--- a/appc-client/code-generator/src/main/resources/templates/open-api/yang-to-open-api.ftl
+++ b/appc-client/code-generator/src/main/resources/templates/open-api/yang-to-open-api.ftl
@@ -21,7 +21,7 @@
ECOMP is a trademark and service mark of AT&T Intellectual Property.
============LICENSE_END=========================================================
-->
-
+<#setting number_format="computer">
<#global _ = " ">
<#global __ = _ + _>
<#global ___ = __ + _>
@@ -162,11 +162,11 @@ ${indent}]
statement or locally (in-line withing a leaf node definition).
-->
<#macro constraints yangType indent = "">
-<#if yangType.patternConstraints?size != 0>
+<#if yangType.patternConstraints?has_content>
${indent},
${indent}"pattern" : "${yangType.patternConstraints?first.regularExpression?replace('\\\\', '\\\\\\\\', 'r')}"
</#if>
-<#if yangType.lengthConstraints?size != 0>
+<#if yangType.lengthConstraints?has_content>
${indent},
${indent}"minLength" : ${yangType.lengthConstraints?first.min},
${indent}"maxLength" : ${yangType.lengthConstraints?first.max}
@@ -234,9 +234,12 @@ ${_}"swagger": "2.0",
${_}"info": {
${__}"version": "${module.QNameModule.formattedRevision}"
<@description obj = module indent = __ />,
+<#if module.contact??>
${__}"contact": {
${_____}"name" : "${module.contact}"
${__}},
+</#if>
+
${__}"title": "${moduleName}"
${_}},
${_}"basePath": "/restconf",