diff --git a/partials/plugins.html b/partials/plugins.html
new file mode 100644
index 00000000000..d95fc9b32dd
--- /dev/null
+++ b/partials/plugins.html
@@ -0,0 +1,72 @@
+
+
+
+
+
+ -
+
+ {{ plugin.name }}
+
+
+ -
+
+ - {{ key }}:
+
+ - {{value}}
+
+
+ -
+ stats:
+
+ -
+
+ - {{key}}:
+ - {{value}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Watching for Changes
+
+
+
+
+
+
+
diff --git a/solr/webapp/web/css/angular/plugins.css b/solr/webapp/web/css/angular/plugins.css
new file mode 100644
index 00000000000..0310e0e5d54
--- /dev/null
+++ b/solr/webapp/web/css/angular/plugins.css
@@ -0,0 +1,212 @@
+/*
+
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You 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.
+
+*/
+
+#content #plugins #navigation
+{
+ width: 20%;
+}
+
+#content #plugins #navigation .cache a { background-image: url( ../../img/ico/disk-black.png ); }
+#content #plugins #navigation .core a { background-image: url( ../../img/ico/wooden-box.png ); }
+#content #plugins #navigation .other a { background-image: url( ../../img/ico/zone.png ); }
+#content #plugins #navigation .highlighting a { background-image: url( ../../img/ico/highlighter-text.png ); }
+#content #plugins #navigation .updatehandler a{ background-image: url( ../../img/ico/arrow-circle.png ); }
+#content #plugins #navigation .queryhandler a { background-image: url( ../../img/ico/magnifier.png ); }
+#content #plugins #navigation .queryparser a { background-image: url( ../../img/ico/asterisk.png ); }
+
+#content #plugins #navigation .PLUGINCHANGES { margin-top: 20px; }
+#content #plugins #navigation .PLUGINCHANGES a { background-image: url( ../../img/ico/eye.png ); }
+#content #plugins #navigation .RELOAD a { background-image: url( ../../img/ico/arrow-circle.png ); }
+
+
+#content #plugins #navigation a
+{
+ position: relative;
+}
+
+#content #plugins #navigation a span
+{
+ background-color: #bba500;
+ border-radius: 5px;
+ color: #fff;
+ font-size: 10px;
+ font-weight: normal;
+ line-height: 1.4em;
+ padding-left: 4px;
+ padding-right: 4px;
+ position: absolute;
+ right: 5px;
+ top: 7px;
+}
+
+#content #plugins #frame
+{
+ float: right;
+ width: 78%;
+}
+
+#content #plugins #frame .entry
+{
+ margin-bottom: 10px;
+}
+
+#content #plugins #frame .entry:last-child
+{
+ margin-bottom: 0;
+}
+
+#content #plugins #frame .entry a
+{
+ background-image: url( ../../img/ico/chevron-small-expand.png );
+ background-position: 0 50%;
+ display: block;
+ font-weight: bold;
+ padding-left: 21px;
+}
+
+#content #plugins #frame .entry.changed a span
+{
+ color: #bba500;
+}
+
+#content #plugins #frame .entry.expanded a
+{
+ background-image: url( ../../img/ico/chevron-small.png );
+}
+
+#content #plugins #frame .entry.expanded ul
+{
+ display: block;
+}
+
+#content #plugins #frame .entry ul
+{
+ border-left: 9px solid #f0f3ff;
+ margin-left: 3px;
+ padding-top: 5px;
+ padding-left: 10px;
+}
+
+#content #plugins #frame .entry li
+{
+ padding-top: 2px;
+ padding-bottom: 2px;
+}
+
+#content #plugins #frame .entry li.stats
+{
+ border-top: 1px solid #c0c0c0;
+ margin-top: 5px;
+ padding-top: 5px;
+}
+
+#content #plugins #frame .entry li.odd
+{
+ background-color: #f8f8f8;
+}
+
+#content #plugins #frame .entry dt,
+#content #plugins #frame .entry .stats span
+{
+ float: left;
+ width: 11%;
+}
+
+#content #plugins #frame .entry dd,
+#content #plugins #frame .entry .stats ul
+{
+ float: right;
+ width: 88%;
+}
+
+#content #plugins #frame .entry .stats ul
+{
+ border-left: 0;
+ margin: 0;
+ padding: 0;
+}
+
+#content #plugins #frame .entry .stats dt
+{
+ width: 27%;
+}
+
+#content #plugins #frame .entry .stats dd
+{
+ width: 72%;
+}
+
+#content #plugins #frame .entry.expanded a.linker {
+ background-image: none;
+ background-position: 0 0;
+ display: inline;
+ font-weight: normal;
+ padding:0px;
+}
+
+#content #plugins #frame .entry.expanded a.linker:hover {
+ background-color:#F0F3FF;
+}
+
+#recording #blockUI
+{
+ position: absolute;
+ left:0;
+ top:0;
+ width: 100%;
+ height: 100%;
+ background-color: #000;
+ opacity: 0.6;
+ z-index:1000;
+ padding:0;
+}
+
+#recording .wrapper
+{
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ padding: 30px;
+ width: 415px;
+ height: 100px;
+ border: 2px solid black;
+ background-color: #FFF;
+ opacity: 1;
+ z-index: 2000;
+ transform: translate(-50%, -50%);
+}
+
+#recording p
+{
+ background-position: 0 50%;
+ float: left;
+ padding-left: 21px;
+ padding-top: 7px;
+ padding-bottom: 7px;
+}
+
+#recording button
+{
+ float: right;
+}
+
+#recording button span
+{
+ background-image: url( ../../img/ico/new-text.png );
+}
diff --git a/solr/webapp/web/index.html b/solr/webapp/web/index.html
index b5e0a9e59e6..b51db287938 100644
--- a/solr/webapp/web/index.html
+++ b/solr/webapp/web/index.html
@@ -34,7 +34,7 @@ limitations under the License.
-
+
@@ -66,6 +66,7 @@ limitations under the License.
+
diff --git a/solr/webapp/web/js/angular/app.js b/solr/webapp/web/js/angular/app.js
index e76febcc83d..b6969d46ae5 100644
--- a/solr/webapp/web/js/angular/app.js
+++ b/solr/webapp/web/js/angular/app.js
@@ -75,6 +75,16 @@ solrAdminApp.config([
templateUrl: 'partials/files.html',
controller: 'FilesController'
}).
+ when('/:core/plugins', {
+ templateUrl: 'partials/plugins.html',
+ controller: 'PluginsController',
+ reloadOnSearch: false
+ }).
+ when('/:core/plugins/:legacytype', {
+ templateUrl: 'partials/plugins.html',
+ controller: 'PluginsController',
+ reloadOnSearch: false
+ }).
when('/:core/query', {
templateUrl: 'partials/query.html',
controller: 'QueryController'
diff --git a/solr/webapp/web/js/angular/controllers/plugins.js b/solr/webapp/web/js/angular/controllers/plugins.js
new file mode 100644
index 00000000000..f80f0dfdeb1
--- /dev/null
+++ b/solr/webapp/web/js/angular/controllers/plugins.js
@@ -0,0 +1,166 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('PluginsController',
+ function($scope, $rootScope, $routeParams, $location, Mbeans) {
+ $scope.resetMenu("plugins");
+
+ if ($routeParams.legacytype) {
+ // support legacy URLs. Angular cannot change #path without reloading controller
+ $location.path("/"+$routeParams.core+"/plugins");
+ $location.search("type", $routeParams.legacytype);
+ return;
+ }
+
+ $scope.refresh = function() {
+ Mbeans.stats({core: $routeParams.core}, function (data) {
+ var type = $location.search().type;
+ $scope.types = getPluginTypes(data, type);
+ $scope.type = getSelectedType($scope.types, type);
+
+ if ($scope.type && $routeParams.entry) {
+ $scope.plugins = $routeParams.entry.split(",");
+ openPlugins($scope.type, $scope.plugins);
+ } else {
+ $scope.plugins = [];
+ }
+ });
+ };
+
+ $scope.selectPluginType = function(type) {
+ $location.search({entry:null, type: type.lower});
+ $scope.type = type;
+ };
+
+ $scope.selectPlugin = function(plugin) {
+ plugin.open = !plugin.open;
+
+ if (plugin.open) {
+ $scope.plugins.push(plugin.name);
+ } else {
+ $scope.plugins.splice($scope.plugins.indexOf(plugin.name), 1);
+ }
+
+ if ($scope.plugins.length==0) {
+ $location.search("entry", null);
+ } else {
+ $location.search("entry", $scope.plugins.join(','));
+ }
+ }
+
+ $scope.startRecording = function() {
+ $scope.isRecording = true;
+ Mbeans.reference({core: $routeParams.core}, function(data) {
+ $scope.reference = data.reference;
+ console.log($scope.reference);
+ })
+ }
+
+ $scope.stopRecording = function() {
+ $scope.isRecording = false;
+ console.log($scope.reference);
+ Mbeans.delta({core: $routeParams.core}, $scope.reference, function(data) {
+ parseDelta($scope.types, data);
+ });
+ }
+
+ $scope.refresh();
+ });
+
+var getPluginTypes = function(data, selected) {
+ var keys = [];
+ var mbeans = data["solr-mbeans"];
+ for (var i=0; i b.name});
+ return keys;
+};
+
+var getPlugins = function(data) {
+ var plugins = [];
+ for (var key in data) {
+ var pluginProperties = data[key];
+ var stats = pluginProperties.stats;
+ delete pluginProperties.stats;
+ for (var stat in stats) {
+ // add breaking space after a bracket or @ to handle wrap long lines:
+ stats[stat] = new String(stats[stat]).replace( /([\(@])/g, '$1');
+ }
+ plugin = {name: key, changed: false, stats: stats, open:false};
+ plugin.properties = pluginProperties;
+ plugins.push(plugin);
+ }
+ plugins.sort(function(a,b) {return a.name > b.name});
+ return plugins;
+};
+
+var getSelectedType = function(types, selected) {
+ if (selected) {
+ for (var i in types) {
+ if (types[i].lower == selected) {
+ return types[i];
+ }
+ }
+ }
+};
+
+var parseDelta = function(types, data) {
+
+ var getByName = function(list, name) {
+ for (var i in list) {
+ if (list[i].name == name) return list[i];
+ }
+ }
+
+ var mbeans = data["solr-mbeans"]
+ for (var i=0; i=0;
+ }
+}
diff --git a/solr/webapp/web/js/angular/services.js b/solr/webapp/web/js/angular/services.js
index 3d833c16ff2..18aec1fa492 100644
--- a/solr/webapp/web/js/angular/services.js
+++ b/solr/webapp/web/js/angular/services.js
@@ -125,7 +125,21 @@ solrAdminServices.factory('System',
}])
.factory('Mbeans',
['$resource', function($resource) {
- return $resource('/solr/:core/admin/mbeans', {'wt':'json', 'stats': true, '_':Date.now()}); // @core
+ return $resource('/solr/:core/admin/mbeans', {'wt':'json', core: '@core', '_':Date.now()}, {
+ stats: {params: {stats: true}},
+ reference: {
+ params: {wt: "xml", stats: true}, transformResponse: function (data) {
+ return {reference: data}
+ }
+ },
+ delta: {method: "POST",
+ params: {stats: true, diff:true},
+ headers: {'Content-type': 'application/x-www-form-urlencoded'},
+ transformRequest: function(data) {
+ return "stream.body=" + encodeURIComponent(data);
+ }
+ }
+ });
}])
.factory('Files',
['$resource', function($resource) {