changeset 224:6568fdab115d

Move Byteman rules view into tabbed subview Reviewed-by: jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-September/025171.html
author Andrew Azores <aazores@redhat.com>
date Mon, 25 Sep 2017 10:31:07 -0400
parents 57a4676a9343
children fc747d2c2f6a
files src/app/components/jvm-info/byteman/byteman.component.js src/app/components/jvm-info/byteman/byteman.controller.js src/app/components/jvm-info/byteman/byteman.controller.spec.js src/app/components/jvm-info/byteman/byteman.html src/app/components/jvm-info/byteman/en.locale.yaml src/app/components/jvm-info/byteman/rules/byteman-rules.component.js src/app/components/jvm-info/byteman/rules/byteman-rules.controller.js src/app/components/jvm-info/byteman/rules/byteman-rules.controller.spec.js src/app/components/jvm-info/byteman/rules/byteman-rules.html src/app/components/jvm-info/byteman/rules/byteman-rules.routing.js src/app/components/jvm-info/byteman/rules/byteman-rules.routing.spec.js src/app/components/jvm-info/byteman/rules/en.locale.yaml
diffstat 12 files changed, 548 insertions(+), 250 deletions(-) [+]
line wrap: on
line diff
--- a/src/app/components/jvm-info/byteman/byteman.component.js	Fri Sep 22 14:33:18 2017 -0400
+++ b/src/app/components/jvm-info/byteman/byteman.component.js	Mon Sep 25 10:31:07 2017 -0400
@@ -26,9 +26,13 @@
  */
 
 import BytemanController from './byteman.controller.js';
+import BytemanRulesComponent from './rules/byteman-rules.component.js';
 
 export default angular
-  .module('byteman', [BytemanController])
+  .module('byteman', [
+    BytemanController,
+    BytemanRulesComponent
+  ])
   .component('byteman', {
     controller: 'BytemanController',
     template: require('./byteman.html')
--- a/src/app/components/jvm-info/byteman/byteman.controller.js	Fri Sep 22 14:33:18 2017 -0400
+++ b/src/app/components/jvm-info/byteman/byteman.controller.js	Mon Sep 25 10:31:07 2017 -0400
@@ -25,75 +25,20 @@
  * exception statement from your version.
  */
 
-import service from './byteman.service.js';
-
 class BytemanController {
-  constructor ($stateParams, $translate, bytemanService) {
+  constructor ($state) {
     'ngInject';
-    this.jvmId = $stateParams.jvmId;
-    this.systemId = $stateParams.systemId;
-    this._translate = $translate;
-    this._svc = bytemanService;
-
-    this.loadedRule = '';
+    this._state = $state;
   }
 
   $onInit () {
-    this._updateRules();
-  }
-
-  $onDestroy () {
-  }
-
-  _updateRules () {
-    return this._svc.getLoadedRules(this.jvmId)
-      .then(res => {
-        this.loadedRule = res;
-        this._clearInput();
-      });
-  }
-
-  _clearInput () {
-    this.ruleText = '';
-  }
-
-  refresh () {
-    return this._updateRules();
-  }
-
-  unload () {
-    if (!this.loadedRule) {
-      return;
+    if (this._state.is('jvmInfo.byteman')) {
+      this._state.go('jvmInfo.byteman.rules');
     }
-    return this._svc.unloadRules(this.systemId, this.jvmId)
-      .then(() => this._updateRules());
-  }
-
-  push () {
-    return this._svc.loadRule(this.systemId, this.jvmId, this.ruleText)
-      .then(() => this._updateRules());
-  }
-
-  pull () {
-    return this._svc.getLoadedRules(this.jvmId)
-      .then(res => {
-        this.loadedRule = res;
-        if (res) {
-          this.ruleText = res;
-        }
-      });
-  }
-
-  generateTemplate () {
-    return this._svc.getJvmMainClass(this.systemId, this.jvmId)
-      .then(mainClass => {
-        return this._translate('byteman.RULE_TEMPLATE', { mainClass: mainClass })
-          .then(res => this.ruleText = res);
-      });
   }
 }
 
 export default angular
-  .module('byteman.controller', [service])
+  .module('byteman.controller', [])
   .controller('BytemanController', BytemanController)
   .name;
--- a/src/app/components/jvm-info/byteman/byteman.controller.spec.js	Fri Sep 22 14:33:18 2017 -0400
+++ b/src/app/components/jvm-info/byteman/byteman.controller.spec.js	Mon Sep 25 10:31:07 2017 -0400
@@ -29,160 +29,42 @@
 
 describe('BytemanController', () => {
 
-  let ctrl, stateParams, translate, svc;
+  let ctrl, state;
   beforeEach(() => {
     angular.mock.module(controllerModule);
 
-    stateParams = {
-      jvmId: 'foo-jvmId',
-      systemId: 'foo-systemId'
+    state = {
+      go: sinon.spy(),
+      is: sinon.stub()
     };
-
-    translate = sinon.stub();
-    translate.then = sinon.stub();
-    translate.returns({ then: translate.then });
-
-    svc = {
-      getLoadedRules: sinon.stub(),
-      loadRule: sinon.stub(),
-      unloadRules: sinon.stub(),
-      getJvmMainClass: sinon.stub()
-    };
-
     angular.mock.inject($controller => {
       'ngInject';
       ctrl = $controller('BytemanController', {
-        $stateParams: stateParams,
-        $translate: translate,
-        bytemanService: svc
+        $state: state
       });
     });
   });
 
   describe('$onInit ()', () => {
-    it('should load injected rules', () => {
-      svc.getLoadedRules.should.not.be.called();
-      svc.getLoadedRules.returns({
-        then: sinon.stub().yields('fake rule')
-      });
+    it('should default to rules state', () => {
+      state.go.should.not.be.called();
+      state.is.should.not.be.called();
+      state.is.returns(true);
 
       ctrl.$onInit();
 
-      svc.getLoadedRules.should.be.calledOnce();
-      svc.getLoadedRules.should.be.calledWith(stateParams.jvmId);
-      ctrl.loadedRule.should.equal('fake rule');
-    });
-  });
-
-  describe('_clearInput ()', () => {
-    it('should reset ruleText property to the empty string', () => {
-      ctrl.ruleText = 'foo';
-      ctrl._clearInput();
-      ctrl.ruleText.should.equal('');
-    });
-  });
-
-  describe('refresh ()', () => {
-    it('should load injected rules', () => {
-      svc.getLoadedRules.should.not.be.called();
-      svc.getLoadedRules.returns({
-        then: sinon.stub().yields('fake rule')
-      });
-
-      ctrl.refresh();
-
-      svc.getLoadedRules.should.be.calledOnce();
-      svc.getLoadedRules.should.be.calledWith(stateParams.jvmId);
-      ctrl.loadedRule.should.equal('fake rule');
-    });
-  });
-
-  describe('unload ()', () => {
-    it('should do nothing if no loaded rule', () => {
-      svc.unloadRules.should.not.be.called();
-      ctrl.unload();
-      svc.unloadRules.should.not.be.called();
+      state.go.should.be.calledOnce();
+      state.go.should.be.calledWith('jvmInfo.byteman.rules');
     });
 
-    it('should unload rules', () => {
-      svc.getLoadedRules.should.not.be.called();
-      svc.getLoadedRules.returns({
-        then: sinon.stub().yields('fake rule')
-      });
-      ctrl.refresh();
-
-      svc.getLoadedRules.returns({
-        then: sinon.stub().yields('')
-      });
-      svc.unloadRules.returns({
-        then: sinon.stub().yields()
-      });
-      ctrl.unload();
-      ctrl.loadedRule.should.equal('');
-    });
-  });
-
-  describe('push ()', () => {
-    it('should send local rule text to service', () => {
-      const injectedRule = 'injected rule';
-      ctrl.ruleText = injectedRule;
-      svc.loadRule.returns({
-        then: sinon.stub().yields()
-      });
-      svc.getLoadedRules.returns({
-        then: sinon.stub().yields(injectedRule)
-      });
-
-      ctrl.push();
-
-      svc.loadRule.should.be.calledOnce();
-      svc.loadRule.should.be.calledWith(stateParams.systemId, stateParams.jvmId, injectedRule);
-      ctrl.loadedRule.should.equal(injectedRule);
-    });
-  });
+    it('should not redirect if URL contains non-rules state', () => {
+      state.go.should.not.be.called();
+      state.is.should.not.be.called();
+      state.is.returns(false);
 
-  describe('pull ()', () => {
-    it('should pull injected rule into editor', () => {
-      const loadedRule = 'loaded rule';
-      svc.getLoadedRules.returns({
-        then: sinon.stub().yields(loadedRule)
-      });
-
-      ctrl.pull();
-
-      svc.getLoadedRules.should.be.calledOnce();
-      svc.getLoadedRules.should.be.calledWith(stateParams.jvmId);
-
-      ctrl.loadedRule.should.equal(loadedRule);
-      ctrl.ruleText.should.equal(loadedRule);
-    });
-
-    it('should not clobber rule text if no remotely injected rules', () => {
-      svc.getLoadedRules.returns({
-        then: sinon.stub().yields()
-      });
+      ctrl.$onInit();
 
-      ctrl.ruleText = 'locally edited rule';
-      ctrl.pull();
-
-      svc.getLoadedRules.should.be.calledOnce();
-      svc.getLoadedRules.should.be.calledWith(stateParams.jvmId);
-
-      should(ctrl.loadedRule).be.undefined();
-      ctrl.ruleText.should.equal('locally edited rule');
-    });
-  });
-
-  describe('generateTemplate ()', () => {
-    it('should set rule text to template', () => {
-      svc.getJvmMainClass.returns({
-        then: sinon.stub().yields('com.example.FooClass')
-      });
-      translate.then.yields('rule template');
-      ctrl.generateTemplate();
-      translate.should.be.calledOnce();
-      translate.should.be.calledWith('byteman.RULE_TEMPLATE', { mainClass: 'com.example.FooClass' });
-      ctrl.ruleText.should.equal('rule template');
+      state.go.should.not.be.called();
     });
   });
 
--- a/src/app/components/jvm-info/byteman/byteman.html	Fri Sep 22 14:33:18 2017 -0400
+++ b/src/app/components/jvm-info/byteman/byteman.html	Mon Sep 25 10:31:07 2017 -0400
@@ -1,40 +1,8 @@
 <div class="container-fluid" style="margin-top: 2vh;">
-  <div class="col-md-8">
-
-    <div class="row">
-      <div role="group" class="btn-group pull-right">
-        <button type="button" class="btn btn-secondary" ng-click="$ctrl.refresh()"><span class="fa fa-refresh"></span></button>
-        <button type="button" class="btn btn-secondary" ng-click="$ctrl.unload()" translate>byteman.UNLOAD_BTN_LABEL</button>
-      </div>
-    </div>
-
-    <div class="row">
-
-      <div class="form-group col-md-6">
-        <label for="localRule" class="label label-info" translate>byteman.LOCAL_RULE_LABEL</label>
-        <textarea name="localRule" class="form-control" ng-model="$ctrl.ruleText" rows="8"/>
-      </div>
-
-      <div role="group" class="btn-group col-md-1">
-        <button type="button" class="btn btn-default"
-                              ng-click="$ctrl.pull()" ng-disabled="!$ctrl.loadedRule" translate>byteman.PULL_BTN_LABEL</button>
-        <button type="button" class="btn btn-default"
-                              ng-click="$ctrl.push()" ng-disabled="!$ctrl.ruleText.length" translate>byteman.PUSH_BTN_LABEL</button>
-      </div>
-
-      <div class="form-group col-md-5">
-        <label for="remoteRule" class="label label-info" translate>byteman.REMOTE_RULE_LABEL</label>
-        <textarea name="remoteRule" class="form-control"
-                                    translate-attr="{placeholder: 'byteman.NO_RULES_LABEL'}" ng-model="$ctrl.loadedRule" rows="8" readonly/>
-      </div>
-
-    </div>
-
-    <div class="row">
-      <div role="group" class="btn-group pull-right">
-        <button type="button" class="btn btn-secondary" ng-click="$ctrl.generateTemplate()" translate>byteman.GENERATE_RULE_BTN_LABEL</button>
-      </div>
-    </div>
-
+  <ul class="nav nav-tabs">
+    <li ui-sref-active="active"><a ui-sref="jvmInfo.byteman.rules" translate>byteman.RULES_VIEW</a></li>
+  </ul>
+  <div class="row">
+    <ui-view></ui-view>
   </div>
 </div>
--- a/src/app/components/jvm-info/byteman/en.locale.yaml	Fri Sep 22 14:33:18 2017 -0400
+++ b/src/app/components/jvm-info/byteman/en.locale.yaml	Mon Sep 25 10:31:07 2017 -0400
@@ -1,18 +1,2 @@
 byteman:
-  LOCAL_RULE_LABEL: Local Rule
-  REMOTE_RULE_LABEL: Injected Rule
-  NO_RULES_LABEL: '<no-rules-loaded>'
-  PUSH_BTN_LABEL: '&gt;'
-  PULL_BTN_LABEL: '&lt;'
-  UNLOAD_BTN_LABEL: Unload Rule
-  GENERATE_RULE_BTN_LABEL: Generate Rule Template
-  RULE_TEMPLATE: |
-    RULE Thermostat byteman template rule for {{mainClass}}
-    CLASS {{mainClass}}
-    METHOD main
-    HELPER org.jboss.byteman.thermostat.helper.ThermostatHelper
-    AT ENTRY
-    IF true
-    DO
-    send("foo-marker", "action", "{{mainClass}}.main() called");
-    ENDRULE
+  RULES_VIEW: Rules
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/jvm-info/byteman/rules/byteman-rules.component.js	Mon Sep 25 10:31:07 2017 -0400
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import BytemanRulesController from './byteman-rules.controller.js';
+
+export default angular
+  .module('byteman.rules', [BytemanRulesController])
+  .component('bytemanRules', {
+    controller: 'BytemanRulesController',
+    template: require('./byteman-rules.html')
+  })
+  .name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/jvm-info/byteman/rules/byteman-rules.controller.js	Mon Sep 25 10:31:07 2017 -0400
@@ -0,0 +1,99 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import service from '../byteman.service.js';
+
+class BytemanRulesController {
+  constructor ($stateParams, $translate, bytemanService) {
+    'ngInject';
+    this.jvmId = $stateParams.jvmId;
+    this.systemId = $stateParams.systemId;
+    this._translate = $translate;
+    this._svc = bytemanService;
+
+    this.loadedRule = '';
+  }
+
+  $onInit () {
+    this._updateRules();
+  }
+
+  $onDestroy () {
+  }
+
+  _updateRules () {
+    return this._svc.getLoadedRules(this.jvmId)
+      .then(res => {
+        this.loadedRule = res;
+        this._clearInput();
+      });
+  }
+
+  _clearInput () {
+    this.ruleText = '';
+  }
+
+  refresh () {
+    return this._updateRules();
+  }
+
+  unload () {
+    if (!this.loadedRule) {
+      return;
+    }
+    return this._svc.unloadRules(this.systemId, this.jvmId)
+      .then(() => this._updateRules());
+  }
+
+  push () {
+    return this._svc.loadRule(this.systemId, this.jvmId, this.ruleText)
+      .then(() => this._updateRules());
+  }
+
+  pull () {
+    return this._svc.getLoadedRules(this.jvmId)
+      .then(res => {
+        this.loadedRule = res;
+        if (res) {
+          this.ruleText = res;
+        }
+      });
+  }
+
+  generateTemplate () {
+    return this._svc.getJvmMainClass(this.systemId, this.jvmId)
+      .then(mainClass => {
+        return this._translate('byteman.rules.RULE_TEMPLATE', { mainClass: mainClass })
+          .then(res => this.ruleText = res);
+      });
+  }
+}
+
+export default angular
+  .module('byteman.rules.controller', [service])
+  .controller('BytemanRulesController', BytemanRulesController)
+  .name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/jvm-info/byteman/rules/byteman-rules.controller.spec.js	Mon Sep 25 10:31:07 2017 -0400
@@ -0,0 +1,189 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import controllerModule from './byteman-rules.controller.js';
+
+describe('BytemanController', () => {
+
+  let ctrl, stateParams, translate, svc;
+  beforeEach(() => {
+    angular.mock.module(controllerModule);
+
+    stateParams = {
+      jvmId: 'foo-jvmId',
+      systemId: 'foo-systemId'
+    };
+
+    translate = sinon.stub();
+    translate.then = sinon.stub();
+    translate.returns({ then: translate.then });
+
+    svc = {
+      getLoadedRules: sinon.stub(),
+      loadRule: sinon.stub(),
+      unloadRules: sinon.stub(),
+      getJvmMainClass: sinon.stub()
+    };
+
+    angular.mock.inject($controller => {
+      'ngInject';
+      ctrl = $controller('BytemanRulesController', {
+        $stateParams: stateParams,
+        $translate: translate,
+        bytemanService: svc
+      });
+    });
+  });
+
+  describe('$onInit ()', () => {
+    it('should load injected rules', () => {
+      svc.getLoadedRules.should.not.be.called();
+      svc.getLoadedRules.returns({
+        then: sinon.stub().yields('fake rule')
+      });
+
+      ctrl.$onInit();
+
+      svc.getLoadedRules.should.be.calledOnce();
+      svc.getLoadedRules.should.be.calledWith(stateParams.jvmId);
+      ctrl.loadedRule.should.equal('fake rule');
+    });
+  });
+
+  describe('_clearInput ()', () => {
+    it('should reset ruleText property to the empty string', () => {
+      ctrl.ruleText = 'foo';
+      ctrl._clearInput();
+      ctrl.ruleText.should.equal('');
+    });
+  });
+
+  describe('refresh ()', () => {
+    it('should load injected rules', () => {
+      svc.getLoadedRules.should.not.be.called();
+      svc.getLoadedRules.returns({
+        then: sinon.stub().yields('fake rule')
+      });
+
+      ctrl.refresh();
+
+      svc.getLoadedRules.should.be.calledOnce();
+      svc.getLoadedRules.should.be.calledWith(stateParams.jvmId);
+      ctrl.loadedRule.should.equal('fake rule');
+    });
+  });
+
+  describe('unload ()', () => {
+    it('should do nothing if no loaded rule', () => {
+      svc.unloadRules.should.not.be.called();
+      ctrl.unload();
+      svc.unloadRules.should.not.be.called();
+    });
+
+    it('should unload rules', () => {
+      svc.getLoadedRules.should.not.be.called();
+      svc.getLoadedRules.returns({
+        then: sinon.stub().yields('fake rule')
+      });
+      ctrl.refresh();
+
+      svc.getLoadedRules.returns({
+        then: sinon.stub().yields('')
+      });
+      svc.unloadRules.returns({
+        then: sinon.stub().yields()
+      });
+      ctrl.unload();
+      ctrl.loadedRule.should.equal('');
+    });
+  });
+
+  describe('push ()', () => {
+    it('should send local rule text to service', () => {
+      const injectedRule = 'injected rule';
+      ctrl.ruleText = injectedRule;
+      svc.loadRule.returns({
+        then: sinon.stub().yields()
+      });
+      svc.getLoadedRules.returns({
+        then: sinon.stub().yields(injectedRule)
+      });
+
+      ctrl.push();
+
+      svc.loadRule.should.be.calledOnce();
+      svc.loadRule.should.be.calledWith(stateParams.systemId, stateParams.jvmId, injectedRule);
+      ctrl.loadedRule.should.equal(injectedRule);
+    });
+  });
+
+  describe('pull ()', () => {
+    it('should pull injected rule into editor', () => {
+      const loadedRule = 'loaded rule';
+      svc.getLoadedRules.returns({
+        then: sinon.stub().yields(loadedRule)
+      });
+
+      ctrl.pull();
+
+      svc.getLoadedRules.should.be.calledOnce();
+      svc.getLoadedRules.should.be.calledWith(stateParams.jvmId);
+
+      ctrl.loadedRule.should.equal(loadedRule);
+      ctrl.ruleText.should.equal(loadedRule);
+    });
+
+    it('should not clobber rule text if no remotely injected rules', () => {
+      svc.getLoadedRules.returns({
+        then: sinon.stub().yields()
+      });
+
+      ctrl.ruleText = 'locally edited rule';
+      ctrl.pull();
+
+      svc.getLoadedRules.should.be.calledOnce();
+      svc.getLoadedRules.should.be.calledWith(stateParams.jvmId);
+
+      should(ctrl.loadedRule).be.undefined();
+      ctrl.ruleText.should.equal('locally edited rule');
+    });
+  });
+
+  describe('generateTemplate ()', () => {
+    it('should set rule text to template', () => {
+      svc.getJvmMainClass.returns({
+        then: sinon.stub().yields('com.example.FooClass')
+      });
+      translate.then.yields('rule template');
+      ctrl.generateTemplate();
+      translate.should.be.calledOnce();
+      translate.should.be.calledWith('byteman.rules.RULE_TEMPLATE', { mainClass: 'com.example.FooClass' });
+      ctrl.ruleText.should.equal('rule template');
+    });
+  });
+
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/jvm-info/byteman/rules/byteman-rules.html	Mon Sep 25 10:31:07 2017 -0400
@@ -0,0 +1,38 @@
+<div class="col-md-8">
+
+  <div class="row">
+    <div role="group" class="btn-group pull-right">
+      <button type="button" class="btn btn-secondary" ng-click="$ctrl.refresh()"><span class="fa fa-refresh"></span></button>
+      <button type="button" class="btn btn-secondary" ng-click="$ctrl.unload()" translate>byteman.rules.UNLOAD_BTN_LABEL</button>
+    </div>
+  </div>
+
+  <div class="row">
+
+    <div class="form-group col-md-6">
+      <label for="localRule" class="label label-info" translate>byteman.rules.LOCAL_RULE_LABEL</label>
+      <textarea name="localRule" class="form-control" ng-model="$ctrl.ruleText" rows="8"/>
+    </div>
+
+    <div role="group" class="btn-group col-md-1">
+      <button type="button" class="btn btn-default"
+                            ng-click="$ctrl.pull()" ng-disabled="!$ctrl.loadedRule" translate>byteman.rules.PULL_BTN_LABEL</button>
+      <button type="button" class="btn btn-default"
+                            ng-click="$ctrl.push()" ng-disabled="!$ctrl.ruleText.length" translate>byteman.rules.PUSH_BTN_LABEL</button>
+    </div>
+
+    <div class="form-group col-md-5">
+      <label for="remoteRule" class="label label-info" translate>byteman.rules.REMOTE_RULE_LABEL</label>
+      <textarea name="remoteRule" class="form-control"
+                                  translate-attr="{placeholder: 'byteman.rules.NO_RULES_LABEL'}" ng-model="$ctrl.loadedRule" rows="8" readonly/>
+    </div>
+
+  </div>
+
+  <div class="row">
+    <div role="group" class="btn-group pull-right">
+      <button type="button" class="btn btn-secondary" ng-click="$ctrl.generateTemplate()" translate>byteman.rules.GENERATE_RULE_BTN_LABEL</button>
+    </div>
+  </div>
+
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/jvm-info/byteman/rules/byteman-rules.routing.js	Mon Sep 25 10:31:07 2017 -0400
@@ -0,0 +1,57 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+function config ($stateProvider) {
+  'ngInject';
+
+  $stateProvider.state('jvmInfo.byteman.rules', {
+    url: '/rules',
+    component: 'bytemanRules',
+    resolve: {
+      lazyLoad: ($q, $ocLazyLoad) => {
+        'ngInject';
+        return $q(resolve => {
+          require.ensure(['./byteman-rules.component.js'], () => {
+            let module = require('./byteman-rules.component.js');
+            $ocLazyLoad.load({ name: module.default });
+            resolve(module);
+          });
+        });
+      }
+    }
+  });
+}
+
+export { config };
+
+export default angular
+  .module('byteman.rules.routing', [
+    'ui.router',
+    'oc.lazyLoad'
+  ])
+  .config(config)
+  .name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/jvm-info/byteman/rules/byteman-rules.routing.spec.js	Mon Sep 25 10:31:07 2017 -0400
@@ -0,0 +1,77 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+describe('BytemanRulesRouting', () => {
+
+  let module = require('./byteman-rules.routing.js');
+  let stateProvider, args, q, ocLazyLoad;
+  beforeEach(() => {
+    angular.mock.module(module.default);
+    stateProvider = {
+      state: sinon.spy()
+    };
+    module.config(stateProvider);
+    args = stateProvider.state.args[0];
+    q = sinon.spy();
+    ocLazyLoad = {
+      load: sinon.spy()
+    };
+  });
+
+  describe('stateProvider', () => {
+    it('should call $stateProvider.state', () => {
+      stateProvider.state.should.be.calledOnce();
+    });
+
+    it('should define a \'jvmInfo.byteman.rules\' state', () => {
+      args[0].should.equal('jvmInfo.byteman.rules');
+    });
+
+    it('should map to /rules', () => {
+      args[1].url.should.equal('/rules');
+    });
+
+    it('resolve should load byteman-rules component', done => {
+      let resolveFn = args[1].resolve.lazyLoad[2];
+      resolveFn.should.be.a.Function();
+      resolveFn(q, ocLazyLoad);
+      q.should.be.calledOnce();
+
+      let deferred = q.args[0][0];
+      deferred.should.be.a.Function();
+
+      let resolve = sinon.stub().callsFake(val => {
+        let mod = require('./byteman-rules.component.js');
+        ocLazyLoad.load.should.be.calledWith({ name: mod.default });
+        val.should.equal(mod);
+        done();
+      });
+      deferred(resolve);
+    });
+  });
+
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/jvm-info/byteman/rules/en.locale.yaml	Mon Sep 25 10:31:07 2017 -0400
@@ -0,0 +1,19 @@
+byteman:
+  rules:
+    LOCAL_RULE_LABEL: Local Rule
+    REMOTE_RULE_LABEL: Injected Rule
+    NO_RULES_LABEL: '<no-rules-loaded>'
+    PUSH_BTN_LABEL: '&gt;'
+    PULL_BTN_LABEL: '&lt;'
+    UNLOAD_BTN_LABEL: Unload Rule
+    GENERATE_RULE_BTN_LABEL: Generate Rule Template
+    RULE_TEMPLATE: |
+      RULE Thermostat byteman template rule for {{mainClass}}
+      CLASS {{mainClass}}
+      METHOD main
+      HELPER org.jboss.byteman.thermostat.helper.ThermostatHelper
+      AT ENTRY
+      IF true
+      DO
+      send("foo-marker", "action", "{{mainClass}}.main() called");
+      ENDRULE