Java tutorial
/* * Copyright 2017-present Open Networking Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onosproject.yang.impl; import com.google.common.io.ByteStreams; import com.google.common.io.Files; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Service; import org.onosproject.yang.YangLiveCompilerService; import org.onosproject.yang.compiler.tool.DefaultYangCompilationParam; import org.onosproject.yang.compiler.tool.YangCompilerManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.FileVisitResult; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import static com.google.common.io.ByteStreams.toByteArray; import static com.google.common.io.Files.createParentDirs; import static com.google.common.io.Files.write; import static java.nio.file.Files.walkFileTree; /** * Represents implementation of YANG live compiler manager. */ @Service @Component(immediate = true) public class YangLiveCompilerManager implements YangLiveCompilerService { private final Logger log = LoggerFactory.getLogger(getClass()); private static final String ZIP_MAGIC = "PK"; @Activate public void activate() { log.info("Started"); } @Deactivate public void deactivate() { log.info("Stopped"); } @Override public InputStream compileYangFiles(String modelId, InputStream yangSources) throws IOException { // Generate temporary directory where the work will happen. File root = Files.createTempDir(); log.info("Compiling YANG model to {}", root); // Unpack the input stream File yangRoot = unpackYangSources(root, yangSources); // Run the YANG compilation phase File javaRoot = runYangCompiler(root, yangRoot, modelId); // Run the Java compilation phase File classRoot = runJavaCompiler(root, javaRoot, modelId); // Run the JAR assembly phase File jarFile = runJarAssembly(root, classRoot, modelId); // Return the final JAR file as input stream return new FileInputStream(jarFile); } // Unpacks the given input stream into the YANG root subdirectory of the specified root directory. private File unpackYangSources(File root, InputStream yangSources) throws IOException { File yangRoot = new File(root, "yang/"); if (yangRoot.mkdirs()) { // Unpack the yang sources into the newly created directory byte[] cache = toByteArray(yangSources); InputStream bis = new ByteArrayInputStream(cache); if (isZipArchive(cache)) { extractZipArchive(yangRoot, bis); } else { extractYangFile(yangRoot, bis); } return yangRoot; } throw new IOException("Unable to create yang source root"); } // Extracts the YANG source stream into the specified directory. private void extractYangFile(File dir, InputStream stream) throws IOException { ByteStreams.copy(stream, new FileOutputStream(new File(dir, "model.yang"))); } // Extracts the ZIP stream into the specified directory. private void extractZipArchive(File dir, InputStream stream) throws IOException { ZipInputStream zis = new ZipInputStream(stream); ZipEntry entry; while ((entry = zis.getNextEntry()) != null) { if (!entry.isDirectory()) { byte[] data = toByteArray(zis); zis.closeEntry(); File file = new File(dir, entry.getName()); createParentDirs(file); write(data, file); } } zis.close(); } // Runs the YANG compiler on the YANG sources in the specified directory. private File runYangCompiler(File root, File yangRoot, String modelId) throws IOException { File javaRoot = new File(root, "java/"); if (javaRoot.mkdirs()) { // Prepare the compilation parameter DefaultYangCompilationParam.Builder param = DefaultYangCompilationParam.builder() .setCodeGenDir(new File(javaRoot, "src").toPath()) .setMetadataGenDir(new File(javaRoot, "schema").toPath()).setModelId(modelId); // TODO: How to convey YANG dependencies? "/dependencies" directory? // Iterate over all files and add all YANG sources. walkFileTree(Paths.get(yangRoot.getAbsolutePath()), new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) throws IOException { if (attributes.isRegularFile() && file.toString().endsWith(".yang")) { param.addYangFile(file); } return FileVisitResult.CONTINUE; } }); // Run the YANG compiler and collect the results new YangCompilerManager().compileYangFiles(param.build()); return javaRoot; } throw new IOException("Unable to create Java results root"); } // Runs the Java compilation on the Java sources generated by YANG compiler. private File runJavaCompiler(File root, File javaRoot, String modelId) throws IOException { File classRoot = new File(root, "classes/"); if (classRoot.mkdirs()) { File compilerScript = writeResource("onos-yang-javac", root); writeResource("YangModelRegistrator.java", root); execute(new String[] { "bash", compilerScript.getAbsolutePath(), javaRoot.getAbsolutePath(), classRoot.getAbsolutePath(), modelId }); return classRoot; } throw new IOException("Unable to create class results root"); } // Run the JAR assembly on the classes root and include any YANG sources as well. private File runJarAssembly(File root, File classRoot, String modelId) throws IOException { File jarFile = new File(root, "model.jar"); File jarScript = writeResource("onos-yang-jar", root); writeResource("app.xml", root); writeResource("features.xml", root); writeResource("YangModelRegistrator.xml", root); execute(new String[] { "bash", jarScript.getAbsolutePath(), classRoot.getAbsolutePath(), jarFile.getAbsolutePath(), modelId }); return jarFile; } // Writes the specified resource as a file in the given directory. private File writeResource(String resourceName, File dir) throws IOException { File script = new File(dir, resourceName); write(toByteArray(getClass().getResourceAsStream("/" + resourceName)), script); return script; } // Indicates whether the stream encoded in the given bytes is a ZIP archive. private boolean isZipArchive(byte[] bytes) { return substring(bytes, ZIP_MAGIC.length()).equals(ZIP_MAGIC); } // Returns the substring of maximum possible length from the specified bytes. private String substring(byte[] bytes, int length) { return new String(bytes, 0, Math.min(bytes.length, length), StandardCharsets.UTF_8); } // Executes the given command arguments as a system command. private void execute(String[] command) throws IOException { try { Process process = Runtime.getRuntime().exec(command); byte[] output = toByteArray(process.getInputStream()); byte[] error = toByteArray(process.getErrorStream()); int code = process.waitFor(); if (code != 0) { log.info("Command failed: status={}, output={}, error={}", code, new String(output), new String(error)); } } catch (InterruptedException e) { log.error("Interrupted executing command {}", command, e); Thread.currentThread().interrupt(); } } }