Integrating YANG live compilation into YANG runtime.

- Bumped ONOS dependency on ONOS YANG tools 2.2.0-b4.
- Added CLI to compile YANG models.
- Added GUI capability to compile YANG models via drag-n-drop or file upload.
- Fixed defect in propagating self-contained JAR apps through the cluster.

Change-Id: Icbd2a588bf1ffe0282e12d3d10a117e0957c3084
diff --git a/apps/yang-gui/src/main/java/org/onosproject/yang/gui/YangModelMessageHandler.java b/apps/yang-gui/src/main/java/org/onosproject/yang/gui/YangModelMessageHandler.java
index 8bf6876..b048744 100644
--- a/apps/yang-gui/src/main/java/org/onosproject/yang/gui/YangModelMessageHandler.java
+++ b/apps/yang-gui/src/main/java/org/onosproject/yang/gui/YangModelMessageHandler.java
@@ -161,7 +161,7 @@
         // FIXME: Hack to properly resolve the YANG source resource
         private InputStream getSource(String modelId, YangModule module) {
             try {
-                module.getYangSource(); // trigger exception
+                return module.getYangSource(); // trigger exception
             } catch (ModelException e) {
                 // Strip the YANG source file base-name and then use it to access
                 // the corresponding resource in the correct run-time context.
@@ -172,7 +172,6 @@
                 return loader == null ? null :
                         loader.getResourceAsStream("/yang/resources" + baseName);
             }
-            return null;
         }
 
         private void addSource(ArrayNode source, InputStream yangSource) {
diff --git a/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel-theme.css b/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel-theme.css
index c26ade5..971e133 100644
--- a/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel-theme.css
+++ b/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel-theme.css
@@ -14,6 +14,11 @@
  * limitations under the License.
  */
 
+/* -- Drag-n-Drop YANG/ZIP files -- */
+div.dropping {
+    border: solid 3px #0095d6;
+}
+
 .light #yang-model-details-panel .src-frame {
     background-color: #f4f4f4;
 }
diff --git a/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel.css b/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel.css
index bf3dce6..f65e636 100644
--- a/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel.css
+++ b/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel.css
@@ -14,11 +14,23 @@
  * limitations under the License.
  */
 
-
 #ov-yang-model h2 {
     display: inline-block;
 }
 
+#ov-yang-model div.ctrl-btns {
+    width: 250px;
+}
+
+/* -- Drag-n-Drop file upload -- */
+#ov-yang-model form#inputYangFileForm,
+#ov-yang-model input#uploadYangFile {
+    display: none;
+}
+
+.dropping {
+
+}
 
 #yang-model-details-panel.floatpanel {
     z-index: 0;
diff --git a/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel.html b/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel.html
index b49bcd8..6cbb0e1 100644
--- a/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel.html
+++ b/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel.html
@@ -1,5 +1,5 @@
 <!-- YANG Model partial HTML -->
-<div id="ov-yang-model">
+<div id="ov-yang-model" yangfiledrop on-file-drop="yangDropped()">
 
     <div class="tabular-header">
         <h2>YANG Models ({{tableData.length}} total)</h2>
@@ -8,6 +8,19 @@
                  icon icon-id="refresh" icon-size="42"
                  tooltip tt-msg="autoRefreshTip"
                  ng-click="toggleRefresh()"></div>
+
+            <div class="separator"></div>
+
+            <form id="inputYangFileForm">
+                <input id="uploadYangFile"
+                       type="file" size="50" accept=".zip, *.jar, *.yang"
+                       yang-file-model="yangFile">
+            </form>
+
+            <div icon icon-size="42" icon-id="nav_yang"
+                 class="active" trigger-yang-form
+                 tooltip tt-msg="uploadTip">
+            </div>
         </div>
     </div>
 
diff --git a/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel.js b/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel.js
index 00b6643..42fa343 100644
--- a/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel.js
+++ b/apps/yang-gui/src/main/resources/app/view/yangModel/yangModel.js
@@ -44,7 +44,8 @@
     // constants
     var pName = 'yang-model-details-panel',
         detailsReq = 'yangModelDetailsRequest',
-        detailsResp = 'yangModelDetailsResponse';
+        detailsResp = 'yangModelDetailsResponse',
+        fileUploadUrl = '/onos/yang/models?modelId=';
 
 
     function createDetailsPanel() {
@@ -135,11 +136,12 @@
     // defines view controller
     angular.module('ovYangModel', [])
     .controller('OvYangModelCtrl', [
-        '$log', '$scope', 'TableBuilderService', 'TableDetailService',
+        '$log', '$scope', '$http', '$timeout',
+        'TableBuilderService', 'TableDetailService',
         'FnService', 'MastService', 'PanelService', 'WebSocketService',
-        'IconService',
+        'IconService', 'UrlFnService', 'FlashService',
 
-        function (_$log_, _$scope_, tbs, tds, _fs_, _mast_, _ps_, _wss_, _is_) {
+        function (_$log_, _$scope_, $http, $timeout, tbs, tds, _fs_, _mast_, _ps_, _wss_, _is_, ufs, _flash_) {
             var handlers = {};
 
             $log = _$log_;
@@ -176,6 +178,45 @@
                 selCb: selCb
             });
 
+            $scope.$on('YangFileChanged', function () {
+                var formData = new FormData(),
+                    url, modelId, finished = false;
+
+                if ($scope.yangFile) {
+                    modelId = $scope.yangFile.name;
+                    modelId = modelId.substr(0, modelId.lastIndexOf("."));
+                    url = fileUploadUrl + modelId;
+                    formData.append('file', $scope.yangFile);
+                    $log.info('Compiling', $scope.yangFile);
+                    d3.select('#frame').classed('dropping', false);
+
+                    // FIXME: Replace this with dialog that shows progress...
+                    for (var i = 0; i < 10; i++) {
+                        $timeout(function () {
+                            if (!finished) _flash_.flash('Compiling ' + modelId);
+                        }, i * 1100);
+                    }
+
+                    $http.post(url, formData, {
+                        transformRequest: angular.identity,
+                        headers: {
+                            'Content-Type': undefined
+                        }
+                    })
+                    .finally(function () {
+                        finished = true;
+                        _flash_.flash('Compile completed for ' + modelId);
+                        $scope.sortCallback($scope.sortParams);
+                        document.getElementById('inputYangFileForm').reset();
+                    });
+                }
+            });
+
+            $scope.yangDropped = function() {
+                $scope.$emit('YangFileChanged');
+                $scope.yangFile = null;
+            };
+
             $scope.$on('$destroy', function () {
                 wss.unbindHandlers(handlers);
             });
@@ -184,6 +225,78 @@
         }
     ])
 
+    // triggers the input form to appear when button is clicked
+    .directive('triggerYangForm', function () {
+        return {
+            restrict: 'A',
+            link: function (scope, elem) {
+                elem.bind('click', function () {
+                    document.getElementById('uploadYangFile')
+                        .dispatchEvent(new MouseEvent('click'));
+                });
+            }
+        };
+    })
+
+    // binds the model file to the scope in scope.yangFile
+    // sends upload request to the server
+    .directive('yangFileModel', ['$parse',
+        function ($parse) {
+            return {
+                restrict: 'A',
+                link: function (scope, elem, attrs) {
+                    var model = $parse(attrs.yangFileModel),
+                        modelSetter = model.assign;
+
+                    elem.bind('change', function () {
+                        scope.$apply(function () {
+                            modelSetter(scope, elem[0].files[0]);
+                        });
+                        scope.$emit('YangFileChanged');
+                    });
+                }
+            };
+        }
+     ])
+
+    .directive("yangfiledrop", ['$parse', '$document', function ($parse, $document) {
+        return {
+            restrict: "A",
+            link: function (scope, element, attrs) {
+                var onYangDrop = $parse(attrs.onFileDrop);
+
+                // When an item is dragged over the document
+                var onDragOver = function (e) {
+                    d3.select('#frame').classed('dropping', true);
+                    e.preventDefault();
+                };
+
+                // When the user leaves the window, cancels the drag or drops the item
+                var onDragEnd = function (e) {
+                    d3.select('#frame').classed('dropping', false);
+                    e.preventDefault();
+                };
+
+                // When a file is dropped
+                var loadFile = function (file) {
+                    scope.yangFile = file;
+                    scope.$apply(onYangDrop(scope));
+                };
+
+                // Dragging begins on the document
+                $document.bind("dragover", onDragOver);
+
+                // Dragging ends on the overlay, which takes the whole window
+                element.bind("dragleave", onDragEnd)
+                    .bind("drop", function (e) {
+                        $log.info('Drag leave', e);
+                        loadFile(e.dataTransfer.files[0]);
+                        onDragEnd(e);
+                    });
+            }
+        };
+    }])
+
     .directive('yangModelDetailsPanel', [
         '$rootScope', '$window', '$timeout', 'KeyService',
 
diff --git a/apps/yang/src/main/java/org/onosproject/yang/YangLiveCompilerManager.java b/apps/yang/src/main/java/org/onosproject/yang/YangLiveCompilerManager.java
deleted file mode 100644
index a4fc857..0000000
--- a/apps/yang/src/main/java/org/onosproject/yang/YangLiveCompilerManager.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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;
-
-import com.google.common.annotations.Beta;
-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.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.Service;
-import org.onosproject.core.CoreService;
-import org.onosproject.yang.compiler.api.YangCompilationParam;
-import org.onosproject.yang.compiler.api.YangCompiledOutput;
-import org.onosproject.yang.compiler.api.YangCompilerException;
-import org.onosproject.yang.compiler.api.YangCompilerService;
-import org.onosproject.yang.compiler.tool.YangCompilerManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-
-/**
- * Represents implementation of YANG live compiler manager.
- */
-@Beta
-@Service
-@Component(immediate = true)
-public class YangLiveCompilerManager implements YangCompilerService {
-
-    private static final String APP_ID = "org.onosproject.yang";
-    private final Logger log = LoggerFactory.getLogger(getClass());
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CoreService coreService;
-
-    @Activate
-    public void activate() {
-        coreService.registerApplication(APP_ID);
-        log.info("Started");
-    }
-
-    @Deactivate
-    public void deactivate() {
-        log.info("Stopped");
-    }
-
-    @Override
-    public YangCompiledOutput compileYangFiles(YangCompilationParam param)
-            throws IOException, YangCompilerException {
-        return new YangCompilerManager().compileYangFiles(param);
-    }
-}
diff --git a/apps/yang/src/main/java/org/onosproject/yang/YangLiveCompilerService.java b/apps/yang/src/main/java/org/onosproject/yang/YangLiveCompilerService.java
new file mode 100644
index 0000000..de151d1
--- /dev/null
+++ b/apps/yang/src/main/java/org/onosproject/yang/YangLiveCompilerService.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import com.google.common.annotations.Beta;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Runtime service for compiling YANG source files and packaging the compiled
+ * artifacts into a ready-to-load application.
+ */
+@Beta
+public interface YangLiveCompilerService {
+
+    /**
+     * Compiles the YANG source file(s) contained in the specified input
+     * stream either as individual YANG source file, or as a JAR/ZIP collection
+     * of YANG source files.
+     *
+     * @param modelId     model identifier
+     * @param yangSources input stream containing a single YANG source file
+     *                    or a JAR/ZIP archive containing collection of YANG
+     *                    source files
+     * @return input stream containing a packaged ONOS application JAR file
+     * @throws IOException if issues arise when reading the yang sources stream
+     */
+    InputStream compileYangFiles(String modelId, InputStream yangSources) throws IOException;
+
+}
diff --git a/apps/yang/src/main/java/org/onosproject/yang/impl/YangCompileCommand.java b/apps/yang/src/main/java/org/onosproject/yang/impl/YangCompileCommand.java
new file mode 100644
index 0000000..3061da6
--- /dev/null
+++ b/apps/yang/src/main/java/org/onosproject/yang/impl/YangCompileCommand.java
@@ -0,0 +1,80 @@
+/*
+ * 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 org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.app.ApplicationAdminService;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.yang.YangLiveCompilerService;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * Compiles the provided YANG source files and installs the resulting model extension.
+ */
+@Command(scope = "onos", name = "compile-model",
+        description = "Compiles the provided YANG source files and installs the resulting model extension")
+public class YangCompileCommand extends AbstractShellCommand {
+
+    @Option(name = "-c", aliases = "--compile-only",
+            description = "Only compile, but do not install the model")
+    private boolean compileOnly = false;
+
+    @Option(name = "-f", aliases = "--force",
+            description = "Force reinstall if already installed")
+    private boolean forceReinstall = false;
+
+    @Argument(index = 0, name = "modelId",
+            description = "Model ID", required = true)
+    String modelId = null;
+
+    @Argument(index = 1, name = "url",
+            description = "URL to the YANG source file(s); .yang, .zip or .jar file",
+            required = true)
+    String url = null;
+
+    @Override
+    protected void execute() {
+        try {
+            InputStream yangJar = new URL(url).openStream();
+            YangLiveCompilerService compiler = get(YangLiveCompilerService.class);
+            if (compileOnly) {
+                ByteStreams.copy(compiler.compileYangFiles(modelId, yangJar), System.out);
+            } else {
+                ApplicationAdminService appService = get(ApplicationAdminService.class);
+
+                if (forceReinstall) {
+                    ApplicationId appId = appService.getId(modelId);
+                    if (appId != null && appService.getApplication(appId) != null) {
+                        appService.uninstall(appId);
+                    }
+                }
+
+                appService.install(compiler.compileYangFiles(modelId, yangJar));
+                appService.activate(appService.getId(modelId));
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+}
diff --git a/apps/yang/src/main/java/org/onosproject/yang/impl/YangLiveCompilerManager.java b/apps/yang/src/main/java/org/onosproject/yang/impl/YangLiveCompilerManager.java
new file mode 100644
index 0000000..d109dc3
--- /dev/null
+++ b/apps/yang/src/main/java/org/onosproject/yang/impl/YangLiveCompilerManager.java
@@ -0,0 +1,232 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/apps/yang/src/main/java/org/onosproject/yang/YangModelsListCommand.java b/apps/yang/src/main/java/org/onosproject/yang/impl/YangModelsListCommand.java
similarity index 79%
rename from apps/yang/src/main/java/org/onosproject/yang/YangModelsListCommand.java
rename to apps/yang/src/main/java/org/onosproject/yang/impl/YangModelsListCommand.java
index 5915e88..69fef62 100644
--- a/apps/yang/src/main/java/org/onosproject/yang/YangModelsListCommand.java
+++ b/apps/yang/src/main/java/org/onosproject/yang/impl/YangModelsListCommand.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.yang;
+package org.onosproject.yang.impl;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -29,11 +29,11 @@
  * Lists registered YANG models.
  */
 @Command(scope = "onos", name = "models",
-         description = "Lists registered YANG models")
+        description = "Lists registered YANG models")
 public class YangModelsListCommand extends AbstractShellCommand {
 
 
-    private static final String FORMAT = "moduleName=%s moduleRevision=%s modelId=%s";
+    private static final String FORMAT = "modelId=%s moduleName=%s moduleRevision=%s";
     private static final String MODULE_NAME = "moduleName";
     private static final String MODULE_REVISION = "moduleRevision";
 
@@ -44,11 +44,11 @@
         if (outputJson()) {
             print("%s", json(service));
         } else {
-            for (YangModel model: service.getModels()) {
+            for (YangModel model : service.getModels()) {
                 for (YangModule module : model.getYangModules()) {
-                    print(FORMAT, module.getYangModuleId().moduleName(),
-                          module.getYangModuleId().revision(),
-                          modelId(model));
+                    print(FORMAT, model.getYangModelId(),
+                          module.getYangModuleId().moduleName(),
+                          module.getYangModuleId().revision());
                 }
             }
         }
@@ -58,9 +58,9 @@
     private JsonNode json(YangModelRegistry service) {
         ObjectMapper mapper = new ObjectMapper();
         ObjectNode result = mapper.createObjectNode();
-        for (YangModel model: service.getModels()) {
+        for (YangModel model : service.getModels()) {
             ArrayNode modelNode = mapper.createArrayNode();
-            result.set(modelId(model), modelNode);
+            result.set(model.getYangModelId(), modelNode);
             for (YangModule module : model.getYangModules()) {
                 ObjectNode moduleNode = mapper.createObjectNode();
                 modelNode.add(moduleNode);
@@ -71,7 +71,4 @@
         return result;
     }
 
-    private String modelId(YangModel m) {
-        return "YM" + Math.abs(m.hashCode());
-    }
 }
diff --git a/apps/yang/src/main/java/org/onosproject/yang/YangRuntimeManager.java b/apps/yang/src/main/java/org/onosproject/yang/impl/YangRuntimeManager.java
similarity index 98%
rename from apps/yang/src/main/java/org/onosproject/yang/YangRuntimeManager.java
rename to apps/yang/src/main/java/org/onosproject/yang/impl/YangRuntimeManager.java
index b9ac2e1..5d9a076 100644
--- a/apps/yang/src/main/java/org/onosproject/yang/YangRuntimeManager.java
+++ b/apps/yang/src/main/java/org/onosproject/yang/impl/YangRuntimeManager.java
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-package org.onosproject.yang;
+package org.onosproject.yang.impl;
 
-import com.google.common.annotations.Beta;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -24,6 +23,7 @@
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
 import org.onosproject.core.CoreService;
+import org.onosproject.yang.YangClassLoaderRegistry;
 import org.onosproject.yang.model.ModelConverter;
 import org.onosproject.yang.model.ModelObjectData;
 import org.onosproject.yang.model.NodeKey;
@@ -62,7 +62,6 @@
 /**
  * Represents implementation of YANG runtime manager.
  */
-@Beta
 @Service
 @Component(immediate = true)
 public class YangRuntimeManager implements YangModelRegistry,
diff --git a/apps/yang/src/main/java/org/onosproject/yang/impl/package-info.java b/apps/yang/src/main/java/org/onosproject/yang/impl/package-info.java
new file mode 100755
index 0000000..5140248
--- /dev/null
+++ b/apps/yang/src/main/java/org/onosproject/yang/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Implementations of various YANG runtime services.
+ */
+package org.onosproject.yang.impl;
\ No newline at end of file
diff --git a/apps/yang/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/yang/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 05cb393f..d5e1711 100644
--- a/apps/yang/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/yang/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -16,7 +16,10 @@
 <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
     <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
         <command>
-            <action class="org.onosproject.yang.YangModelsListCommand"/>
+            <action class="org.onosproject.yang.impl.YangModelsListCommand"/>
+        </command>
+        <command>
+            <action class="org.onosproject.yang.impl.YangCompileCommand"/>
         </command>
     </command-bundle>
 </blueprint>
diff --git a/apps/yang/src/main/resources/YangModelRegistrator.java b/apps/yang/src/main/resources/YangModelRegistrator.java
new file mode 100644
index 0000000..fb6b0e3
--- /dev/null
+++ b/apps/yang/src/main/resources/YangModelRegistrator.java
@@ -0,0 +1,10 @@
+// Auto-generated code
+package PACKAGE;
+
+import org.onosproject.yang.AbstractYangModelRegistrator;
+
+public class YangModelRegistrator extends AbstractYangModelRegistrator {
+    public YangModelRegistrator() {
+        super(YangModelRegistrator.class);
+    }
+}
\ No newline at end of file
diff --git a/apps/yang/src/main/resources/YangModelRegistrator.xml b/apps/yang/src/main/resources/YangModelRegistrator.xml
new file mode 100644
index 0000000..647ae45
--- /dev/null
+++ b/apps/yang/src/main/resources/YangModelRegistrator.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="PACKAGE.YangModelRegistrator" activate="activate" deactivate="deactivate">
+    <implementation class="PACKAGE.YangModelRegistrator"/>
+    <property name="service.pid" value="PACKAGE.YangModelRegistrator"/>
+    <reference name="modelRegistry" interface="org.onosproject.yang.runtime.YangModelRegistry" cardinality="1..1" policy="static" bind="bindModelRegistry" unbind="unbindModelRegistry"/>
+    <reference name="sourceResolver" interface="org.onosproject.yang.YangClassLoaderRegistry" cardinality="1..1" policy="static" bind="bindSourceResolver" unbind="unbindSourceResolver"/>
+</scr:component>
\ No newline at end of file
diff --git a/apps/yang/src/main/resources/app.xml b/apps/yang/src/main/resources/app.xml
new file mode 100644
index 0000000..1188a02
--- /dev/null
+++ b/apps/yang/src/main/resources/app.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<app name="MODEL_ID" origin="ONOS Community" version="1.0"
+     title="YANG Model: MODEL_ID" category="Model" url="http://onosproject.org"
+     featuresRepo="mvn:org.onosproject/MODEL_ID/1.0/xml/features"
+     features="MODEL_ID" apps="org.onosproject.yang">
+    <description>YANG Model: MODEL_ID</description>
+    <artifact>mvn:org.onosproject/MODEL_ID/1.0</artifact>
+</app>
\ No newline at end of file
diff --git a/apps/yang/src/main/resources/features.xml b/apps/yang/src/main/resources/features.xml
new file mode 100644
index 0000000..7d56a53
--- /dev/null
+++ b/apps/yang/src/main/resources/features.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
+          name="MODEL_ID-1.0">
+    <feature name="MODEL_ID" version="1.0"
+             description="YANG Model: MODEL_ID">
+        <feature>onos-api</feature>
+        <bundle>wrap:mvn:org.onosproject/MODEL_ID/1.0$Bundle-SymbolicName=MODEL_ID&amp;Bundle-Version=1.0&amp;Service-Component=OSGI-INF/MODEL_ID.YangModelRegistrator.xml</bundle>
+    </feature>
+</features>
\ No newline at end of file
diff --git a/apps/yang/src/main/resources/onos-yang-jar b/apps/yang/src/main/resources/onos-yang-jar
new file mode 100644
index 0000000..4429e07
--- /dev/null
+++ b/apps/yang/src/main/resources/onos-yang-jar
@@ -0,0 +1,24 @@
+#!/bin/bash
+# -----------------------------------------------------------------------------
+# Assembles ONOS application jar from the compiled YANG model artifacts.
+# -----------------------------------------------------------------------------
+
+classes=${1:-classes}
+jar=${2:-model.jar}
+modelId=${3-org.onosproject.model.unknown}
+
+# Generate the app.xml descriptor file and stage it at the top of the jar
+sed "s/MODEL_ID/$modelId/g" $(dirname $0)/app.xml > $classes/app.xml
+
+# Generate features.xml file and stage it at the top of the jar
+sed "s/MODEL_ID/$modelId/g" $(dirname $0)/features.xml > $classes/features.xml
+mkdir -p $classes/m2/org/onosproject/$modelId/1.0/
+cp $classes/features.xml $classes/m2/org/onosproject/$modelId/1.0/$modelId-1.0-features.xml
+
+# Generate model self-registrator SCR descriptor
+mkdir -p $classes/OSGI-INF
+sed "s/PACKAGE/$modelId/g" $(dirname $0)/YangModelRegistrator.xml \
+    > $classes/OSGI-INF/$modelId.YangModelRegistrator.xml
+
+cd $classes
+jar cf $jar *
\ No newline at end of file
diff --git a/apps/yang/src/main/resources/onos-yang-javac b/apps/yang/src/main/resources/onos-yang-javac
new file mode 100755
index 0000000..1fa344c
--- /dev/null
+++ b/apps/yang/src/main/resources/onos-yang-javac
@@ -0,0 +1,35 @@
+#!/bin/bash
+# -----------------------------------------------------------------------------
+# Compiles Java code generated by the YANG compiler.
+# -----------------------------------------------------------------------------
+
+sources=${1:-src}
+classes=${2:-classes}
+modelId=${3:-org.onosproject.model.unknown}
+
+# Stage the YANG sources and the YANG schema meta-data
+mkdir -p $classes/yang/resources
+cp $sources/schema/* $classes/yang/resources
+
+root=${root:-system}
+op="$root/org/onosproject"
+
+function findLib() {
+    find $1/$2 -name "$2-*.jar"
+}
+
+classPath="$(findLib $op onos-yang-model)"
+classPath="$classPath:$(findLib $op onos-yang-runtime)"
+classPath="$classPath:$(findLib $op onos-yang-compiler-api)"
+classPath="$classPath:$(findLib $op onos-apps-yang)"
+
+package=${modelId//./\/}
+mkdir -p $sources/src/$package
+sed "s/PACKAGE/$modelId/g" $(dirname $0)/YangModelRegistrator.java \
+    > $sources/src/$package/YangModelRegistrator.java
+
+# Compile the generated java code
+find $sources/src -name '*.java' > $sources/javaFiles
+javac -source 1.8 -target 1.8 \
+        -sourcepath "$sources/src" -classpath "$classPath" -d "$classes" \
+        @$sources/javaFiles
diff --git a/apps/yang/web/BUCK b/apps/yang/web/BUCK
index e6a782f..9563765 100644
--- a/apps/yang/web/BUCK
+++ b/apps/yang/web/BUCK
@@ -6,6 +6,7 @@
   '//lib:onos-yang-model',
   '//lib:onos-yang-compiler-api',
   '//lib:onos-yang-runtime',
+  '//apps/yang:onos-apps-yang'
 ]
 
 TEST_DEPS = [
diff --git a/apps/yang/web/src/main/java/org/onosproject/yang/web/YangWebResource.java b/apps/yang/web/src/main/java/org/onosproject/yang/web/YangWebResource.java
index 72d9cbe..9e56f4a 100644
--- a/apps/yang/web/src/main/java/org/onosproject/yang/web/YangWebResource.java
+++ b/apps/yang/web/src/main/java/org/onosproject/yang/web/YangWebResource.java
@@ -20,7 +20,10 @@
 import org.glassfish.jersey.media.multipart.FormDataBodyPart;
 import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
 import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.FormDataParam;
+import org.onosproject.app.ApplicationAdminService;
 import org.onosproject.rest.AbstractWebResource;
+import org.onosproject.yang.YangLiveCompilerService;
 import org.onosproject.yang.compiler.api.YangCompilationParam;
 import org.onosproject.yang.compiler.api.YangCompilerService;
 import org.onosproject.yang.compiler.datamodel.YangNode;
@@ -32,8 +35,10 @@
 import org.onosproject.yang.runtime.YangModelRegistry;
 
 import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import java.io.File;
@@ -74,11 +79,31 @@
     /**
      * Compiles and registers the given yang files.
      *
+     * @param modelId model identifier
+     * @param stream YANG, ZIP or JAR file
+     * @return 200 OK
+     * @throws IOException when fails to generate a file
+     */
+    @POST
+    @Consumes(MediaType.MULTIPART_FORM_DATA)
+    public Response upload(@QueryParam("modelId") @DefaultValue("org.onosproject.model.unknown") String modelId,
+                           @FormDataParam("file") InputStream stream) throws IOException {
+        YangLiveCompilerService compiler = get(YangLiveCompilerService.class);
+        ApplicationAdminService appService = get(ApplicationAdminService.class);
+        appService.install(compiler.compileYangFiles(modelId, stream));
+        appService.activate(appService.getId(modelId));
+        return Response.ok().build();
+    }
+
+    /**
+     * Compiles and registers the given yang files.
+     *
      * @param formData YANG files or ser files
      * @return 200 OK
      * @throws IOException when fails to generate a file
      */
     @POST
+    @Path("deprecated")
     @Consumes(MediaType.MULTIPART_FORM_DATA)
     public Response upload(FormDataMultiPart formData) throws IOException {
         Map<String, List<File>> input = parseInputData(formData);
@@ -161,7 +186,7 @@
     }
 
     private void addToParam(DefaultYangCompilationParam.Builder builder,
-                                            File file) {
+                            File file) {
         if (file.getName().endsWith(YANG_FILE_EXTENSION)) {
             builder.addYangFile(Paths.get(file.getAbsolutePath()));
         } else if (file.getName().endsWith(SER_FILE_EXTENSION)) {
@@ -198,8 +223,7 @@
 
         // first get all directories,
         // then make those directory on the destination Path
-        for (Enumeration<? extends ZipEntry> enums = zip.entries();
-             enums.hasMoreElements();) {
+        for (Enumeration<? extends ZipEntry> enums = zip.entries(); enums.hasMoreElements();) {
             ZipEntry entry = enums.nextElement();
 
             String fileName = YANG_RESOURCES + entry.getName();
@@ -211,8 +235,7 @@
         }
 
         //now create all files
-        for (Enumeration<? extends ZipEntry> enums = zip.entries();
-             enums.hasMoreElements();) {
+        for (Enumeration<? extends ZipEntry> enums = zip.entries(); enums.hasMoreElements();) {
             ZipEntry entry = enums.nextElement();
             String fileName = YANG_RESOURCES + entry.getName();
             File f = new File(fileName);
diff --git a/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java b/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java
index b02d33a..ec38607 100644
--- a/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java
+++ b/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java
@@ -262,6 +262,9 @@
     public synchronized InputStream getApplicationInputStream(String appName) {
         try {
             File appFile = appFile(appName, appName + OAR);
+            if (!appFile.exists()) {
+                appFile = appFile(appName, appName + JAR);
+            }
             return new FileInputStream(appFile.exists() ? appFile : appFile(appName, APP_XML));
         } catch (FileNotFoundException e) {
             throw new ApplicationException("Application " + appName + " not found");
@@ -383,7 +386,7 @@
 
         // Create the file directory structure and copy the file there.
         File jar = appFile(desc.name(), jarName);
-        boolean ok = jar.getParentFile().mkdirs();
+        boolean ok = jar.getParentFile().exists() || jar.getParentFile().mkdirs();
         if (ok) {
             Files.write(toByteArray(stream), jar);
             Files.copy(appFile(desc.name(), FEATURES_XML), appFile(desc.name(), featuresName));
@@ -402,7 +405,9 @@
             cfg.setAttributeSplittingDisabled(true);
             cfg.setDelimiterParsingDisabled(true);
             cfg.load(appFile(desc.name(), FEATURES_XML));
-            return cfg.getString("feature.bundle");
+            return cfg.getString("feature.bundle")
+                    .replaceFirst("wrap:", "")
+                    .replaceFirst("\\$Bundle-.*$", "");
         } catch (ConfigurationException e) {
             log.warn("Self-contained application {} has no features.xml", desc.name());
             return null;
diff --git a/lib/BUCK b/lib/BUCK
index ef24796..1013dcf 100644
--- a/lib/BUCK
+++ b/lib/BUCK
@@ -1,4 +1,4 @@
-# ***** This file was auto-generated at Mon, 31 Jul 2017 23:06:54 GMT. Do not edit this file manually. *****
+# ***** This file was auto-generated at Tue, 8 Aug 2017 17:39:19 GMT. Do not edit this file manually. *****
 # ***** Use onos-lib-gen *****
 
 pass_thru_pom(
@@ -1211,55 +1211,55 @@
 
 remote_jar (
   name = 'onos-yang-model',
-  out = 'onos-yang-model-2.2.0-b3.jar',
-  url = 'mvn:org.onosproject:onos-yang-model:jar:2.2.0-b3',
-  sha1 = '27e93661796c37268a09aa5c4cdc2e2f6e0ec975',
-  maven_coords = 'org.onosproject:onos-yang-model:2.2.0-b3',
+  out = 'onos-yang-model-2.2.0-b4.jar',
+  url = 'mvn:org.onosproject:onos-yang-model:jar:2.2.0-b4',
+  sha1 = 'a1ebb2e81770b2c55cfc2fd9f6e935403bf329b7',
+  maven_coords = 'org.onosproject:onos-yang-model:2.2.0-b4',
   visibility = [ 'PUBLIC' ],
 )
 
 remote_jar (
   name = 'onos-yang-compiler-api',
-  out = 'onos-yang-compiler-api-2.2.0-b3.jar',
-  url = 'mvn:org.onosproject:onos-yang-compiler-api:jar:2.2.0-b3',
-  sha1 = 'eeb41cec4779b3a03c684af4fd176711ba3d999f',
-  maven_coords = 'org.onosproject:onos-yang-compiler-api:2.2.0-b3',
+  out = 'onos-yang-compiler-api-2.2.0-b4.jar',
+  url = 'mvn:org.onosproject:onos-yang-compiler-api:jar:2.2.0-b4',
+  sha1 = 'b0d7f8569dc14c2444c20b32aebc133be4b31719',
+  maven_coords = 'org.onosproject:onos-yang-compiler-api:2.2.0-b4',
   visibility = [ 'PUBLIC' ],
 )
 
 remote_jar (
   name = 'onos-yang-runtime',
-  out = 'onos-yang-runtime-2.2.0-b3.jar',
-  url = 'mvn:org.onosproject:onos-yang-runtime:jar:2.2.0-b3',
-  sha1 = '40dc7df958b5b7779425522c0ac5dccd7c90f325',
-  maven_coords = 'org.onosproject:onos-yang-runtime:2.2.0-b3',
+  out = 'onos-yang-runtime-2.2.0-b4.jar',
+  url = 'mvn:org.onosproject:onos-yang-runtime:jar:2.2.0-b4',
+  sha1 = '82eeb6c0ecd3e9181f08e6309364e008efd90c73',
+  maven_coords = 'org.onosproject:onos-yang-runtime:2.2.0-b4',
   visibility = [ 'PUBLIC' ],
 )
 
 remote_jar (
   name = 'onos-yang-serializers-json',
-  out = 'onos-yang-serializers-json-2.2.0-b3.jar',
-  url = 'mvn:org.onosproject:onos-yang-serializers-json:jar:2.2.0-b3',
-  sha1 = 'c922e6caf738debf3c82a5e950150dcee3691a68',
-  maven_coords = 'org.onosproject:onos-yang-serializers-json:2.2.0-b3',
+  out = 'onos-yang-serializers-json-2.2.0-b4.jar',
+  url = 'mvn:org.onosproject:onos-yang-serializers-json:jar:2.2.0-b4',
+  sha1 = 'ca24ff1719a3a546220b5a5346d8602fd955026c',
+  maven_coords = 'org.onosproject:onos-yang-serializers-json:2.2.0-b4',
   visibility = [ 'PUBLIC' ],
 )
 
 remote_jar (
   name = 'onos-yang-serializers-xml',
-  out = 'onos-yang-serializers-xml-2.2.0-b3.jar',
-  url = 'mvn:org.onosproject:onos-yang-serializers-xml:jar:2.2.0-b3',
-  sha1 = '941c0f451f77f4eeb87f34bb4f8c7c95fab558b2',
-  maven_coords = 'org.onosproject:onos-yang-serializers-xml:2.2.0-b3',
+  out = 'onos-yang-serializers-xml-2.2.0-b4.jar',
+  url = 'mvn:org.onosproject:onos-yang-serializers-xml:jar:2.2.0-b4',
+  sha1 = '0ecfff253688a2d11574e34f3edf14a8bef25790',
+  maven_coords = 'org.onosproject:onos-yang-serializers-xml:2.2.0-b4',
   visibility = [ 'PUBLIC' ],
 )
 
 remote_jar (
   name = 'onos-yang-serializers-utils',
-  out = 'onos-yang-serializers-utils-2.2.0-b3.jar',
-  url = 'mvn:org.onosproject:onos-yang-serializers-utils:jar:2.2.0-b3',
-  sha1 = 'a46292313584ff174b3a7feb998d54253bbb7383',
-  maven_coords = 'org.onosproject:onos-yang-serializers-utils:2.2.0-b3',
+  out = 'onos-yang-serializers-utils-2.2.0-b4.jar',
+  url = 'mvn:org.onosproject:onos-yang-serializers-utils:jar:2.2.0-b4',
+  sha1 = 'e48c4b374fce66defa1fbe23e9d60e3e78cb48de',
+  maven_coords = 'org.onosproject:onos-yang-serializers-utils:2.2.0-b4',
   visibility = [ 'PUBLIC' ],
 )
 
diff --git a/lib/deps.json b/lib/deps.json
index eacea31..5dd2f70 100644
--- a/lib/deps.json
+++ b/lib/deps.json
@@ -24,7 +24,7 @@
       "slf4j-api",
       "osgi-core",
       "org.osgi.compendium",
-      { "name": "org.apache.felix.scr.annotations", "compile_only": true },
+      "org.apache.felix.scr.annotations",
       "org.apache.felix.scr",
       "jackson-annotations",
       "jackson-core",
@@ -231,12 +231,12 @@
     "onos-yang-datamodel": "mvn:org.onosproject:onos-yang-datamodel:1.11",
     "onos-yang-maven-plugin": "mvn:org.onosproject:onos-yang-maven-plugin:1.11",
     "onos-yang-utils-generator": "mvn:org.onosproject:onos-yang-utils-generator:1.11",
-    "onos-yang-model":"mvn:org.onosproject:onos-yang-model:2.2.0-b3",
-    "onos-yang-compiler-api":"mvn:org.onosproject:onos-yang-compiler-api:2.2.0-b3",
-    "onos-yang-runtime":"mvn:org.onosproject:onos-yang-runtime:2.2.0-b3",
-    "onos-yang-serializers-json":"mvn:org.onosproject:onos-yang-serializers-json:2.2.0-b3",
-    "onos-yang-serializers-xml":"mvn:org.onosproject:onos-yang-serializers-xml:2.2.0-b3",
-    "onos-yang-serializers-utils":"mvn:org.onosproject:onos-yang-serializers-utils:2.2.0-b3",
+    "onos-yang-model":"mvn:org.onosproject:onos-yang-model:2.2.0-b4",
+    "onos-yang-compiler-api":"mvn:org.onosproject:onos-yang-compiler-api:2.2.0-b4",
+    "onos-yang-runtime":"mvn:org.onosproject:onos-yang-runtime:2.2.0-b4",
+    "onos-yang-serializers-json":"mvn:org.onosproject:onos-yang-serializers-json:2.2.0-b4",
+    "onos-yang-serializers-xml":"mvn:org.onosproject:onos-yang-serializers-xml:2.2.0-b4",
+    "onos-yang-serializers-utils":"mvn:org.onosproject:onos-yang-serializers-utils:2.2.0-b4",
     "org.apache.servicemix.bundles.dom4j":"mvn:org.apache.servicemix.bundles:org.apache.servicemix.bundles.dom4j:1.6.1_5",
     "plexus-utils": "mvn:org.codehaus.plexus:plexus-utils:3.0.24",
     "sshd-core": "mvn:org.apache.sshd:sshd-core:1.4.0",
diff --git a/tools/dev/bin/patch-yang-libs b/tools/dev/bin/patch-yang-libs
index be082d5..01f3935 100755
--- a/tools/dev/bin/patch-yang-libs
+++ b/tools/dev/bin/patch-yang-libs
@@ -3,7 +3,7 @@
 # Patches lib/BUCK file to use locally built YANG tools.
 # -----------------------------------------------------------------------------
 
-BVER=2.1
+BVER=2.2.0-b3
 SVER=2.2-SNAPSHOT
 
 YANG_TOOLS_ROOT=~/onos-yang-tools
diff --git a/tools/package/runtime/bin/onos-compile-yang b/tools/package/runtime/bin/onos-compile-yang
new file mode 100755
index 0000000..855167c
--- /dev/null
+++ b/tools/package/runtime/bin/onos-compile-yang
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+#
+# Copyright 2015-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.
+#
+
+# -----------------------------------------------------------------------------
+# Tool to compile the specified YANG file(s) using the ONOS live compilation.
+# -----------------------------------------------------------------------------
+
+# If ONOS_HOME is set, respect its value.
+# If ONOS_HOME is not set (e.g. in the init or service environment),
+# set it based on this script's path.
+ONOS_HOME=${ONOS_HOME:-$(cd $(dirname $0)/.. >/dev/null 2>&1 && pwd)}
+ONOS_WEB_USER=${ONOS_WEB_USER:-onos} # ONOS WEB User defaults to 'onos'
+ONOS_WEB_PASS=${ONOS_WEB_PASS:-rocks} # ONOS WEB Password defaults to 'rocks'
+
+. ${ONOS_HOME}/bin/_find-node
+
+node=$(find_node $1)
+yang=$2
+
+export URL=http://$node:8181/onos/yang/models
+export curl="curl -sS --user $ONOS_WEB_USER:$ONOS_WEB_PASS --noproxy localhost "
+
+# Prints usage help
+function usage {
+    echo "usage: onos-compile-yang <yang-file|zip-file|jar-file|directory>" >&2
+    exit 1
+}
+
+[ -z $node -o "$node" = "-h" -o "$node" = "--help" -o "$node" = "-?" ] && usage
+
+if [ -d $yang ]; then
+    newYang=/tmp/$(basename $yang).jar
+    jar cf $newYang $yang
+    yang=$newYang
+fi
+
+file=$(basename $yang)
+modelId="$(echo $file | sed -E 's/(.zip|.jar|.yang)$//g')"
+curl $URL?modelId=$modelId -F"file=@$yang"
diff --git a/web/gui/src/main/webapp/app/fw/widget/table.js b/web/gui/src/main/webapp/app/fw/widget/table.js
index 1e52017..1d8f596 100644
--- a/web/gui/src/main/webapp/app/fw/widget/table.js
+++ b/web/gui/src/main/webapp/app/fw/widget/table.js
@@ -82,11 +82,11 @@
         if (haveItems) {
             setTdWidths(tableElems.thead, width);
             setTdWidths(tableElems.tbody, width);
-            setHeight(tableElems.thead, tableElems.table.select('.table-body'), height);
         } else {
             setTdWidths(tableElems.thead, width);
             _width(tableElems.tbody, width + 'px');
         }
+        setHeight(tableElems.thead, tableElems.table.select('.table-body'), height);
     }
 
     // sort columns state model and functions
diff --git a/web/gui/src/main/webapp/app/view/app/app.html b/web/gui/src/main/webapp/app/view/app/app.html
index 8f65fc4..6eaa126 100644
--- a/web/gui/src/main/webapp/app/view/app/app.html
+++ b/web/gui/src/main/webapp/app/view/app/app.html
@@ -15,7 +15,7 @@
 
             <form id="inputFileForm">
                 <input id="uploadFile"
-                       type="file" size="50" accept=".oar"
+                       type="file" size="50" accept=".oar,.jar"
                        file-model="appFile">
             </form>