ARTEMIS-1270 Management Console - Hawtio Solution

Add Artemis Plugin
This commit is contained in:
Andy Taylor 2017-07-05 16:34:18 +01:00 committed by Clebert Suconic
parent cadf909f78
commit fa7b247dc4
28 changed files with 3719 additions and 0 deletions

View File

@ -0,0 +1,3 @@
# artemis-hawtio
Is a HawtIO plugin that allows the viewing and manipulation of topic/queues and other JMS resources.

View File

@ -0,0 +1,263 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-hawtio-pom</artifactId>
<version>2.2.0-SNAPSHOT</version>
</parent>
<artifactId>artemis-plugin</artifactId>
<name>ActiveMQ Artemis HawtIO Plugin</name>
<!-- hawtio plugins are almost always war files -->
<packaging>war</packaging>
<properties>
<activemq.basedir>${project.basedir}/../..</activemq.basedir>
<!-- filtered plugin properties, we don't define plugin-scripts here
as we build that dynamically using maven-antrun-plugin below. -->
<!-- plugin-context is what context this plugin will handle requests on
in the application server -->
<plugin-context>/artemis-plugin</plugin-context>
<!-- plugin-name is the name of our plugin, affects the name used for
the plugin's mbean -->
<plugin-name>${project.artifactId}</plugin-name>
<!-- plugin-domain is currently unused, we just define it to an empty
string -->
<plugin-domain/>
<!-- this lets this plugin deploy nicely into karaf, these get used
for the ImportPackage directive for maven-bundle-plugin -->
<fuse.osgi.import>
javax.servlet,
*;resolution:=optional
</fuse.osgi.import>
<webapp-dir>${project.artifactId}-${project.version}</webapp-dir>
<webapp-outdir>${basedir}/target/${webapp-dir}</webapp-outdir>
<schema-outdir>${basedir}/src/main/webapp/lib</schema-outdir>
<appjs-outfile>${webapp-outdir}/app/app.js</appjs-outfile>
</properties>
<dependencies>
<!-- we only need to embed this dependency in the war, this contains
a nice helper class that our plugin can use to export it's plugin
mbean -->
<dependency>
<groupId>io.hawt</groupId>
<artifactId>hawtio-plugin-mbean</artifactId>
</dependency>
<!-- servlet API is provided by the container -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<!-- we want to ensure src/main/resources/WEB-INF/web.xml is being filtered
so that it picks up all of our javascript files -->
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<plugins>
<!-- We use maven-antrun-plugin to build up a list of
javascript files for our plugin mbean, this means
it needs to run before the maven-resources-plugin
copies and filters the web.xml, since for this
example we use contextParam settings to configure
our plugin mbean -->
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>${maven-antrun-plugin-version}</version>
<executions>
<execution>
<!-- we run this early in the build process before
maven-resources-plugin is run. We're exporting the
plugin-scripts property from here, so we need to
use maven-antrun-plugin 1.6 or up -->
<id>generate-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<echo>Building plugin javascript file list</echo>
<!-- javascript-files will contain all of the javascript in
our project -->
<fileset id="javascript-files" dir="${basedir}/src/main/webapp">
<include name="**/*.js"/>
</fileset>
<!-- we need to strip out the top level path which is
our source directory and be sure to change the directory
separators to forward slashes -->
<pathconvert pathsep="," dirsep="/" property="plugin-scripts" refid="javascript-files">
<map from="${basedir}/src/main/webapp/" to=""/>
</pathconvert>
<echo>Files: ${plugin-scripts}</echo>
</target>
<!-- this exports plugin-scripts to the maven build, without
this line ${plugin-scripts} in the web.xml file won't be
replaced -->
<exportAntProperties>true</exportAntProperties>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>${maven-resources-plugin-version}</version>
<executions>
<execution>
<!-- defining this maven plugin in the same phase as the
maven-antrun-plugin but *after* we've configured the
maven-antrun-plugin ensures we filter resources *after*
we've discovered the plugin .js files. -->
<id>copy-resources</id>
<phase>generate-sources</phase>
<goals>
<goal>resources</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- maven-bundle-plugin config, needed to make this war
deployable in karaf, defines the context that this bundle
should handle requests on -->
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>${maven-bundle-plugin-version}</version>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<manifestLocation>${webapp-outdir}/META-INF</manifestLocation>
<supportedProjectTypes>
<supportedProjectType>jar</supportedProjectType>
<supportedProjectType>bundle</supportedProjectType>
<supportedProjectType>war</supportedProjectType>
</supportedProjectTypes>
<instructions>
<Webapp-Context>${plugin-context}</Webapp-Context>
<Web-ContextPath>${plugin-context}</Web-ContextPath>
<Embed-Directory>WEB-INF/lib</Embed-Directory>
<Embed-Dependency>*;scope=compile|runtime</Embed-Dependency>
<Embed-Transitive>true</Embed-Transitive>
<Export-Package>${fuse.osgi.export}</Export-Package>
<Import-Package>${fuse.osgi.import}</Import-Package>
<DynamicImport-Package>${fuse.osgi.dynamic}</DynamicImport-Package>
<Private-Package>${fuse.osgi.private.pkg}</Private-Package>
<Bundle-ClassPath>.,WEB-INF/classes</Bundle-ClassPath>
<Bundle-Name>${project.description}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Implementation-Title>HawtIO</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
</instructions>
</configuration>
</plugin>
<!-- We define the maven-war-plugin here and make sure it uses
the manifest file generated by the maven-bundle-plugin. We
also ensure it picks up our filtered web.xml and not the one
in src/main/resources -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${war-plugin-version}</version>
<configuration>
<outputFileNameMapping>@{artifactId}@-@{baseVersion}@@{dashClassifier?}@.@{extension}@</outputFileNameMapping>
<packagingExcludes>**/classes/OSGI-INF/**</packagingExcludes>
<failOnMissingWebXml>false</failOnMissingWebXml>
<archive>
<manifestFile>${webapp-outdir}/META-INF/MANIFEST.MF</manifestFile>
</archive>
<webResources>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<excludes>
<exclude>log4j.properties</exclude>
</excludes>
</resource>
</webResources>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,71 @@
/*
* 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.
*/
package org.apache.activemq.hawtio.plugin;
import io.hawt.web.plugin.HawtioPlugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* The Plugin Context Listener used to load in the plugin
**/
public class PluginContextListener implements ServletContextListener {
private static final Logger LOG = LoggerFactory.getLogger(PluginContextListener.class);
HawtioPlugin plugin = null;
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext context = servletContextEvent.getServletContext();
plugin = new HawtioPlugin();
plugin.setContext((String)context.getInitParameter("plugin-context"));
plugin.setName(context.getInitParameter("plugin-name"));
plugin.setScripts(context.getInitParameter("plugin-scripts"));
plugin.setDomain(null);
try {
plugin.init();
} catch (Exception e) {
throw createServletException(e);
}
LOG.info("Initialized {} plugin", plugin.getName());
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
try {
plugin.destroy();
} catch (Exception e) {
throw createServletException(e);
}
LOG.info("Destroyed {} plugin", plugin.getName());
}
protected RuntimeException createServletException(Exception e) {
return new RuntimeException(e);
}
}

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<description>An Artemis Plugin</description>
<display-name>An Artemis plugin</display-name>
<context-param>
<description>Plugin's path on the server</description>
<param-name>plugin-context</param-name>
<param-value>${plugin-context}</param-value>
</context-param>
<context-param>
<description>Plugin's path on the server</description>
<param-name>plugin-name</param-name>
<param-value>${project.artifactId}</param-value>
</context-param>
<context-param>
<description>Plugin's path on the server</description>
<param-name>plugin-domain</param-name>
<param-value>${plugin-domain}</param-value>
</context-param>
<context-param>
<description>Plugin's path on the server</description>
<param-name>plugin-scripts</param-name>
<param-value>${plugin-scripts}</param-value>
</context-param>
<listener>
<listener-class>org.apache.activemq.hawtio.plugin.PluginContextListener</listener-class>
</listener>
</web-app>

View File

@ -0,0 +1,23 @@
## ---------------------------------------------------------------------------
## 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.
## ---------------------------------------------------------------------------
log4j.rootLogger=INFO, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%-5p | %t | %m%n

View File

@ -0,0 +1,19 @@
### Artemis
Click [Artemis](#/jmx/attributes?tab=artemis) in the top navigation bar to see the Artemis specific plugin. (The Artemis tab won't appear if there is no broker in this JVM). The Artemis plugin works very much the same as the JMX plugin however with a focus on interacting with an Artemis broker.
The tree view on the left-hand side shows the top level JMX tree of each broker instance running in the JVM. Expanding the tree will show the various MBeans registered by Artemis that you can inspect via the **Attributes** tab.
#### Creating a new Address
To create a new address simply click on the broker or the address folder in the jmx tree and click on the create tab.
Once you have created an address you should be able to **Send** to it by clicking on it in the jmx tree and clicking on the send tab.
#### Creating a new Queue
To create a new queue click on the address you want to bind the queue to and click on the create tab.
Once you have created a queue you should be able to **Send** a message to it or **Browse** it or view the **Attributes** or **Charts**. Simply click on the queue in th ejmx tree and click on the appropriate tab.
You can also see a graphical view of all brokers, addresses, queues and their consumers using the **Diagram** tab.

View File

@ -0,0 +1,42 @@
<!--
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.
Architecture
-->
<script type="text/ng-template" id="header">
<div class="tree-header" ng-controller="ARTEMIS.TreeHeaderController">
<div class="left">
</div>
<div class="right">
<i class="icon-chevron-down clickable"
title="Expand all nodes"
ng-click="expandAll()"></i>
<i class="icon-chevron-up clickable"
title="Unexpand all nodes"
ng-click="contractAll()"></i>
</div>
</div>
</script>
<hawtio-pane position="left" width="300" header="header">
<div id="tree-container"
ng-controller="Jmx.MBeansController">
<div id="artemistree"
ng-controller="ARTEMIS.TreeController"></div>
</div>
</hawtio-pane>
<div class="row-fluid">
<ng-include src="'app/jmx/html/subLevelTabs.html'"></ng-include>
<div id="properties" ng-view></div>
</div>

View File

@ -0,0 +1,313 @@
<!--
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.
Architecture
-->
<style type="text/css">
.span4.node-panel {
margin-top: 10px;
margin-left: 10px;
width: 33%;
}
.node-attributes dl {
margin-top: 5px;
margin-bottom: 10px;
}
.node-attributes dt {
width: 150px;
}
.node-attributes dd {
margin-left: 160px;
}
.node-attributes dd a {
/** lets make the destination links wrap */
-ms-word-break: break-all;
word-break: break-all;
-webkit-hyphens: auto;
-moz-hyphens: auto;
hyphens: auto;
}
ul.viewMenu li {
padding-left: 10px;
padding-top: 2px;
padding-bottom: 2px;
}
div#pop-up {
display: none;
position: absolute;
color: white;
font-size: 14px;
background: rgba(0, 0, 0, 0.6);
padding: 5px 10px 5px 10px;
-moz-border-radius: 8px 8px;
border-radius: 8px 8px;
}
div#pop-up-title {
font-size: 15px;
margin-bottom: 4px;
font-weight: bolder;
}
div#pop-up-content {
font-size: 12px;
}
rect.graphbox {
fill: #FFF;
}
rect.graphbox.frame {
stroke: #222;
stroke-width: 2px
}
/* only things directly related to the network graph should be here */
path.link {
fill: none;
stroke: #666;
stroke-width: 1.5px; b
}
marker.broker {
stroke: red;
fill: red;
stroke-width: 1.5px;
}
circle.broker {
fill: #0c0;
}
circle.brokerSlave {
fill: #c00;
}
circle.notActive {
fill: #c00;
}
path.link.group {
stroke: #ccc;
}
marker#group {
stroke: #ccc;
fill: #ccc;
}
circle.group {
fill: #eee;
stroke: #ccc;
}
circle.destination {
fill: #bbb;
stroke: #ccc;
}
circle.pinned {
stroke-width: 4.5px;
}
path.link.profile {
stroke-dasharray: 0, 2 1;
stroke: #888;
}
marker#container {
}
circle.container {
stroke-dasharray: 0, 2 1;
stroke: #888;
}
path.link.container {
stroke-dasharray: 0, 2 1;
stroke: #888;
}
circle {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
cursor: pointer;
}
circle.closeMode {
cursor: crosshair;
}
path.link.destination {
stroke: #ccc;
}
circle.topic {
fill: #c0c;
}
circle.queue {
fill: #00c;
}
circle.consumer {
fill: #cfc;
}
circle.producer {
fill: #ccf;
}
path.link.producer {
stroke: #ccc;
}
path.link.consumer {
stroke: #ccc;
}
path.link.network {
stroke: #ccc;
}
circle.selected {
stroke-width: 3px;
}
.selected {
stroke-width: 3px;
}
text {
font: 10px sans-serif;
pointer-events: none;
}
text.shadow {
stroke: #fff;
stroke-width: 3px;
stroke-opacity: .8;
}
</style>
<div class="row-fluid mq-page" ng-controller="ARTEMIS.BrokerDiagramController">
<div ng-hide="inDashboard" class="span12 page-padded">
<div class="section-header">
<div class="section-filter">
<input type="text" class="search-query" placeholder="Filter..." ng-model="searchFilter">
<i class="icon-remove clickable" title="Clear filter" ng-click="searchFilter = ''"></i>
</div>
<div class="section-controls">
<a href="#"
class="dropdown-toggle"
data-toggle="dropdown">
View &nbsp;<i class="icon-caret-down"></i>
</a>
<ul class="dropdown-menu viewMenu">
<li>
<label class="checkbox">
<input type="checkbox" ng-model="viewSettings.consumer"> Consumers
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox" ng-model="viewSettings.producer"> Producers
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox" ng-model="viewSettings.address"> Addresses
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox" ng-model="viewSettings.queue"> Queues
</label>
</li>
<li class="divider"></li>
<li>
<label class="checkbox">
<input type="checkbox" ng-model="viewSettings.broker"> Brokers
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox" ng-model="viewSettings.slave"> Slave brokers
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox" ng-model="viewSettings.network"> Networks
</label>
</li>
<li class="divider"></li>
<li title="Should we show the details panel on the left">
<label class="checkbox">
<input type="checkbox" ng-model="viewSettings.panel"> Details panel
</label>
</li>
<li title="Show the summary popup as you hover over nodes">
<label class="checkbox">
<input type="checkbox" ng-model="viewSettings.popup"> Hover text
</label>
</li>
<li title="Show the labels next to nodes">
<label class="checkbox">
<input type="checkbox" ng-model="viewSettings.label"> Label
</label>
</li>
</ul>
</div>
</div>
</div>
<div id="pop-up">
<div id="pop-up-title"></div>
<div id="pop-up-content"></div>
</div>
<div class="row-fluid">
<div class="{{viewSettings.panel ? 'span8' : 'span12'}} canvas broker-canvas">
<div hawtio-force-graph graph="graph" selected-model="selectedNode" link-distance="150" charge="-600" nodesize="10" marker-kind="marker-end"
style="min-height: 800px">
</div>
</div>
<div ng-show="viewSettings.panel" class="span4 node-panel">
<div ng-show="selectedNode" class="node-attributes">
<dl ng-repeat="property in selectedNodeProperties" class="dl-horizontal">
<dt title="{{property.key}}">{{property.key}}:</dt>
<dd ng-bind-html-unsafe="property.value"></dd>
</dl>
</div>
</div>
</div>
<div ng-include="'app/fabric/html/connectToContainerDialog.html'"></div>
</div>

View File

@ -0,0 +1,156 @@
<!--
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.
Architecture
-->
<div ng-controller="ARTEMIS.BrowseQueueController">
<div class="row-fluid">
<div class="span6">
<input class="search-query span12" type="text" ng-model="gridOptions.filterOptions.filterText"
placeholder="Filter messages">
</div>
<div class="span6">
<div class="pull-right">
<form class="form-inline">
<button class="btn" ng-disabled="!gridOptions.selectedItems.length" ng-show="dlq" ng-click="retryMessages()"
title="Moves the dead letter queue message back to its original destination so it can be retried" data-placement="bottom">
<i class="icon-reply"></i> Retry
</button>
<button class="btn" ng-disabled="gridOptions.selectedItems.length !== 1" ng-click="resendMessage()"
title="Edit the message to resend it" data-placement="bottom">
<i class="icon-share-alt"></i> Resend
</button>
<button class="btn" ng-disabled="!gridOptions.selectedItems.length" ng-click="moveDialog = true"
title="Move the selected messages to another destination" data-placement="bottom">
<i class="icon-share-alt"></i> Move
</button>
<button class="btn" ng-disabled="!gridOptions.selectedItems.length"
ng-click="deleteDialog = true"
title="Delete the selected messages">
<i class="icon-remove"></i> Delete
</button>
<button class="btn" ng-click="refresh()"
title="Refreshes the list of messages">
<i class="icon-refresh"></i>
</button>
</form>
</div>
</div>
</div>
<div class="row-fluid">
<div class="gridStyle" ng-grid="gridOptions"></div>
</div>
<div hawtio-slideout="showMessageDetails" title="{{row.JMSMessageID}}">
<div class="dialog-body">
<div class="row-fluid">
<div class="pull-right">
<form class="form-horizontal no-bottom-margin">
<div class="btn-group"
hawtio-pager="messages"
on-index-change="selectRowIndex"
row-index="rowIndex"></div>
<button class="btn" ng-disabled="!gridOptions.selectedItems.length" ng-click="moveDialog = true"
title="Move the selected messages to another destination" data-placement="bottom">
<i class="icon-share-alt"></i> Move
</button>
<button class="btn btn-danger" ng-disabled="!gridOptions.selectedItems.length"
ng-click="deleteDialog = true"
title="Delete the selected messages">
<i class="icon-remove"></i> Delete
</button>
<button class="btn" ng-click="showMessageDetails = !showMessageDetails" title="Close this dialog">
<i class="icon-remove"></i> Close
</button>
</form>
</div>
</div>
<div class="row-fluid">
<div class="expandable closed">
<div title="Headers" class="title">
<i class="expandable-indicator"></i> Headers & Properties
</div>
<div class="expandable-body well">
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>Header</th>
<th>Value</th>
</tr>
</thead>
<tbody ng-bind-html-unsafe="row.headerHtml">
</tbody>
<!--
<tr ng-repeat="(key, value) in row.headers">
<td class="property-name">{{key}}</td>
<td class="property-value">{{value}}</td>
</tr>
-->
</table>
</div>
</div>
</div>
<div class="row-fluid">
<div>Displaying body as <span ng-bind="row.textMode"></span></div>
<div hawtio-editor="row.bodyText" read-only="true" mode='mode'></div>
</div>
</div>
</div>
<div hawtio-confirm-dialog="deleteDialog"
ok-button-text="Delete"
on-ok="deleteMessages()">
<div class="dialog-body">
<p>You are about to delete
<ng-pluralize count="gridOptions.selectedItems.length"
when="{'1': 'a message!', 'other': '{} messages!'}">
</ng-pluralize>
</p>
<p>This operation cannot be undone so please be careful.</p>
</div>
</div>
<div hawtio-confirm-dialog="moveDialog"
ok-button-text="Move"
on-ok="moveMessages()">
<div class="dialog-body">
<p>Move
<ng-pluralize count="gridOptions.selectedItems.length"
when="{'1': 'message', 'other': '{} messages'}"></ng-pluralize>
to: <input type="text" ng-model="queueName" placeholder="Queue name"
typeahead="title.unescapeHTML() for title in queueNames($viewValue) | filter:$viewValue" typeahead-editable='true'></p>
<p>
You cannot undo this operation.<br>
Though after the move you can always move the
<ng-pluralize count="gridOptions.selectedItems.length"
when="{'1': 'message', 'other': 'messages'}"></ng-pluralize>
back again.
</p>
</div>
</div>
</div>

View File

@ -0,0 +1,44 @@
<!--
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.
Architecture
-->
<form class="form-horizontal" ng-controller="ARTEMIS.AddressController">
<div class="control-group">
<label class="control-label" for="addressName"
title="The routing name of this address">Address name</label>
<div class="controls">
<input id="addressName" type="text" maxlength="300" ng-model="addressName" placeholder="Address Name"/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="routingType">Routing type</label>
<div class="controls">
<select id="routingType" ng-model="routingType">
<option value='0'>Multicast</option>
<option value='1'>Anycast</option>
<option value='2'>Both</option>
</select>
</div>
</div>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn btn-info" ng-click="createAddress(addressName, routingType)" ng-disabled="!addressName">Create Address</button>
</div>
</div>
</form>

View File

@ -0,0 +1,77 @@
<!--
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.
Architecture
-->
<form class="form-horizontal" ng-controller="ARTEMIS.QueueController">
<div class="control-group">
<label class="control-label" for="queueName"
title="The routing name of this address">Queue name</label>
<div class="controls">
<input id="queueName" type="text" maxlength="300" ng-model="queueName" placeholder="Queue Name"/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="routingType">Routing type</label>
<div class="controls">
<select id="routingType" ng-model="routingType">
<option value='0'>Multicast</option>
<option value='1'>Anycast</option>
</select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="durable"
title="Whether the queue will be durable">Durable</label>
<div class="controls">
<input id="durable" type="checkbox" ng-model="durable" value="false">
</div>
</div>
<div class="control-group">
<label class="control-label" for="filter"
title="The user name to be used when connecting to the broker">Filter</label>
<div class="controls">
<input id="filter" type="text" maxlength="300" ng-model="filter" placeholder="Filter"/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="maxConsumers"
title="The maximum consumers the queue can have">Max Consumers</label>
<div class="controls">
<input id="maxConsumers" type="number" ng-model="maxConsumers" placeholder="maxConsumers"/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="purgeWhenNoConsumers"
title="Whether or not this queue should be purged (emptied and paused) when there are no consumers">Purge when no consumers</label>
<div class="controls">
<input id="purgeWhenNoConsumers" type="checkbox" ng-model="purgeWhenNoConsumers" value="false"/>
</div>
</div>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn btn-info" ng-click="createQueue(queueName, routingType, durable, filter, maxConsumers, purgeWhenNoConsumers)" ng-disabled="!queueName">Create Queue</button>
</div>
</div>
</form>

View File

@ -0,0 +1,48 @@
<!--
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.
Architecture
-->
<div ng-controller="ARTEMIS.AddressController">
<div class="row-fluid">
<div class="control-group">
<div class="alert">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong>Warning:</strong> these operations cannot be undone. Please be careful!
</div>
</div>
</div>
<div class="row-fluid">
<div class="span4">
<div class="control-group">
<button type="submit" class="btn btn-warning" ng-click="deleteDialog = true">Delete address '{{name().unescapeHTML()}}'</button>
<label>This will remove the address completely.</label>
</div>
</div>
</div>
<div hawtio-confirm-dialog="deleteDialog"
ok-button-text="Delete"
on-ok="deleteAddress()">
<div class="dialog-body">
<p>You are about to delete the <b>{{name().unescapeHTML()}}</b> address</p>
<p>This operation cannot be undone so please be careful.</p>
</div>
</div>
</div>

View File

@ -0,0 +1,62 @@
<!--
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.
Architecture
-->
<div ng-controller="ARTEMIS.QueueController">
<div class="row-fluid">
<div class="control-group">
<div class="alert">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong>Warning:</strong> these operations cannot be undone. Please be careful!
</div>
</div>
</div>
<div class="row-fluid">
<div class="span4">
<div class="control-group">
<button type="submit" class="btn btn-warning" ng-click="deleteDialog = true">Delete queue '{{name().unescapeHTML()}}'</button>
<label>This will remove the queue completely.</label>
</div>
</div>
<div class="span4">
<div class="control-group">
<button type="submit" class="btn btn-warning" ng-click="purgeDialog = true">Purge queue '{{name().unescapeHTML()}}'</button>
<label>Purges all the current messages on the queue.</label>
</div>
</div>
</div>
<div hawtio-confirm-dialog="deleteDialog"
ok-button-text="Delete"
on-ok="deleteDestination(true)">
<div class="dialog-body">
<p>You are about to delete the <b>{{name().unescapeHTML()}}</b> queue</p>
<p>This operation cannot be undone so please be careful.</p>
</div>
</div>
<div hawtio-confirm-dialog="purgeDialog"
ok-button-text="Purge"
on-ok="purgeDestination()">
<div class="dialog-body">
<p>You are about to purge the <b>{{name().unescapeHTML()}}</b> queue</p>
<p>This operation cannot be undone so please be careful.</p>
</div>
</div>
</div>

View File

@ -0,0 +1,69 @@
<!--
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.
Architecture
-->
<div ng-controller="ARTEMIS.PreferencesController">
<form class="form-horizontal">
<div class="control-group">
<label class="control-label" for="artemisUserName"
title="The user name to be used when connecting to the broker">User name</label>
<div class="controls">
<input id="artemisUserName" type="text" placeholder="username" ng-model="artemisUserName" autofill/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="artemisPassword" title="Password to be used when connecting to the broker">Password</label>
<div class="controls">
<input id="artemisPassword" type="password" placeholder="password" ng-model="artemisPassword" autofill/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="artemisDLQ" title="The DLQ of the Broker">DLQ</label>
<div class="controls">
<input id="artemisDLQ" type="text" placeholder="DLQ" ng-model="artemisDLQ" autofill/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="artemisExpiryQueue" title="The Expiry Queue of the Broker">Expiry Queue</label>
<div class="controls">
<input id="artemisExpiryQueue" type="text" placeholder="ExpiryQueue" ng-model="artemisExpiryQueue" autofill/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="byteMessages">Browse byte messages</label>
<div class="controls">
<select id="byteMessages" ng-model="activemqBrowseBytesMessages">
<option value='99'>Off</option>
<option value='8'>Decimal</option>
<option value='4'>Hex</option>
<option value='2'>Decimal and text</option>
<option value='1'>Hex and text</option>
</select>
<span class="help-block">Browsing byte messages should display the message body as</span>
</div>
</div>
</form>
</div>

View File

@ -0,0 +1,135 @@
<!--
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.
Architecture
-->
<div ng-controller="ARTEMIS.SendMessageController">
<div class="tabbable" ng-model="tab">
<div value="compose" class="tab-pane" title="Compose">
<!--
title="Compose a new message to send"
-->
<div class="row-fluid">
<span ng-show="noCredentials" class="alert">
No credentials set for endpoint! Please set your username and password in the <a
href="" ng-click="openPrefs()">Preferences</a> page
</span>
<form class="form-inline pull-right">
<div class="row-fluid">
<div class="controls">
<label class="control-label" for="durable" title="Is this message durable">Durable: </label>
<input id="durable" type="checkbox" ng-model="durable" value="true">
</div>
<button class="btn" ng-click="addHeader()" title="Add a new message header"><i
class="icon-plus"></i> Header
</button>
<button type="submit" class="btn btn-primary" ng-click="sendMessage(durable)">Send message</button>
</form>
</div>
<form class="form-inline bottom-margin" ng-submit="addHeader()">
<ol class="zebra-list header-list">
<div class="row-fluid">
<li ng-repeat="header in headers">
<div class="span4">
<input type="text" style="width: 100%" class="headerName"
ng-model="header.name"
typeahead="completion for completion in defaultHeaderNames() | filter:$viewValue"
typeahead-editable='true'
placeholder="Header name">
</div>
<div class="span6">
<input type="text" style="width: 100%" ng-model="header.value"
placeholder="Value of the message header">
</div>
<div class="span2">
<button type="submit" class="btn" title="Add a new message header">
<i class="icon-plus"></i>
</button>
<button type="button" ng-click="removeHeader(header)" class="btn" title="Removes this message header">
<i class="icon-remove"></i>
</button>
</div>
</li>
</div>
</ol>
</form>
<div class="row-fluid">
<form class="form-inline">
<div class="controls">
<label class="control-label" for="sourceFormat" title="The text format to use for the message payload">Payload
format: </label>
<select ng-model="codeMirrorOptions.mode.name" id="sourceFormat">
<option value="javascript">JSON</option>
<option value="text" selected>Plain text</option>
<option value="properties">Properties</option>
<option value="xml">XML</option>
</select>
<button class="btn" ng-click="autoFormat()"
title="Automatically pretty prints the message so its easier to read">Auto format
</button>
</div>
</form>
</div>
<div class="row-fluid">
<textarea ui-codemirror="codeMirrorOptions" ng-model="message"></textarea>
</div>
</div>
</tab>
<div ng-switch="showChoose">
<div ng-switch-when="true">
<div value="choose" class="tab-pane" title="Choose">
<!--
title="Choose messages to send from the available files in the Profile configuration for this container">
-->
<div class="row-fluid bottom-margin">
<span ng-show="noCredentials" class="alert">
No credentials set for endpoint! Please set your username and password in the <a
href="#/preferences">Preferences</a> page
</span>
<button type="submit" ng-disabled="!fileSelection().length" class="btn btn-primary pull-right"
ng-click="sendSelectedFiles()">
<ng-pluralize count="fileSelection().length"
when="{'0': 'No files selected', '1': 'Send the file','other': 'Send {} files'}">
</ng-pluralize>
</button>
</div>
<p>Choose which files to send from the profile configuration:</p>
<div class="control-group inline-block">
<input class="search-query" type="text" ng-model="searchText" placeholder="Filter..." autofocus>
</div>
<ul>
<li ng-repeat="fileName in profileFileNames | filter:searchText">
<input type="checkbox" ng-model="selectedFiles[fileName]"> {{fileName}}
</li>
</ul>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,119 @@
/*
* 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.
*/
/**
* @module ARTEMIS
*/
var ARTEMIS = (function(ARTEMIS) {
/**
* @method AddressController
* @param $scope
* @param ARTEMISService
*
* Controller for the Create interface
*/
ARTEMIS.AddressController = function ($scope, workspace, ARTEMISService, jolokia, localStorage) {
Core.initPreferenceScope($scope, localStorage, {
'routingType': {
'value': 0,
'converter': parseInt,
'formatter': parseInt
}
});
var artemisJmxDomain = localStorage['artemisJmxDomain'] || "org.apache.activemq.artemis";
$scope.workspace = workspace;
$scope.message = "";
$scope.deleteDialog = false;
$scope.$watch('workspace.selection', function () {
workspace.moveIfViewInvalid();
});
function operationSuccess() {
$scope.addressName = "";
$scope.workspace.operationCounter += 1;
Core.$apply($scope);
Core.notification("success", $scope.message);
$scope.workspace.loadTree();
}
function deleteSuccess() {
// lets set the selection to the parent
workspace.removeAndSelectParentNode();
$scope.workspace.operationCounter += 1;
Core.$apply($scope);
Core.notification("success", $scope.message);
$scope.workspace.loadTree();
}
$scope.createAddress = function (name, routingType) {
var mbean = getBrokerMBean(jolokia);
if (mbean) {
if (routingType == 0) {
$scope.message = "Created Multicast Address " + name;
ARTEMIS.log.info($scope.message);
ARTEMISService.artemisConsole.createAddress(mbean, jolokia, name, "MULTICAST", onSuccess(operationSuccess));
}
else if (routingType == 1) {
$scope.message = "Created Anycast Address " + name;
ARTEMIS.log.info($scope.message);
ARTEMISService.artemisConsole.createAddress(mbean, jolokia, name, "ANYCAST", onSuccess(operationSuccess));
}
else {
$scope.message = "Created Anycast/Multicast Address " + name;
ARTEMIS.log.info($scope.message);
ARTEMISService.artemisConsole.createAddress(mbean, jolokia, name, "ANYCAST,MULTICAST", onSuccess(operationSuccess));
}
}
};
$scope.deleteAddress = function () {
var selection = workspace.selection;
var entries = selection.entries;
var mbean = getBrokerMBean(jolokia);
ARTEMIS.log.info(mbean);
if (mbean) {
if (selection && jolokia && entries) {
var domain = selection.domain;
var name = entries["name"];
name = name.unescapeHTML();
if (name.charAt(0) === '"' && name.charAt(name.length -1) === '"')
{
name = name.substr(1,name.length -2);
}
ARTEMIS.log.info(name);
var operation;
$scope.message = "Deleted address " + name;
ARTEMISService.artemisConsole.deleteAddress(mbean, jolokia, name, onSuccess(deleteSuccess));
}
}
};
$scope.name = function () {
var selection = workspace.selection;
if (selection) {
return selection.title;
}
return null;
};
function getBrokerMBean(jolokia) {
var mbean = null;
var selection = workspace.selection;
var folderNames = selection.folderNames;
mbean = "" + folderNames[0] + ":broker=" + folderNames[1];
ARTEMIS.log.info("broker=" + mbean);
return mbean;
}
};
return ARTEMIS;
} (ARTEMIS || {}));

View File

@ -0,0 +1,126 @@
/*
* 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.
*/
var ARTEMIS;
(function (ARTEMIS) {
ARTEMIS.log = Logger.get("ARTEMIS");
ARTEMIS.jmxDomain = 'org.apache.ARTEMIS';
function getSelectionQueuesFolder(workspace) {
function findQueuesFolder(node) {
if (node) {
if (node.title === "Queues" || node.title === "Queue") {
return node;
}
var parent = node.parent;
if (parent) {
return findQueuesFolder(parent);
}
}
return null;
}
var selection = workspace.selection;
if (selection) {
return findQueuesFolder(selection);
}
return null;
}
ARTEMIS.getSelectionQueuesFolder = getSelectionQueuesFolder;
function getSelectionTopicsFolder(workspace) {
function findTopicsFolder(node) {
var answer = null;
if (node) {
if (node.title === "Topics" || node.title === "Topic") {
answer = node;
}
if (answer === null) {
angular.forEach(node.children, function (child) {
if (child.title === "Topics" || child.title === "Topic") {
answer = child;
}
});
}
}
return answer;
}
var selection = workspace.selection;
if (selection) {
return findTopicsFolder(selection);
}
return null;
}
ARTEMIS.getSelectionTopicsFolder = getSelectionTopicsFolder;
/**
* Sets $scope.row to currently selected JMS message.
* Used in:
* - ARTEMIS/js/browse.ts
* - camel/js/browseEndpoint.ts
*
* TODO: remove $scope argument and operate directly on other variables. but it's too much side effects here...
*
* @param message
* @param key unique key inside message that distinguishes between values
* @param $scope
*/
function selectCurrentMessage(message, key, $scope) {
// clicking on message's link would interfere with messages selected with checkboxes
$scope.gridOptions.selectAll(false);
var idx = Core.pathGet(message, ["rowIndex"]);
var jmsMessageID = Core.pathGet(message, ["entity", key]);
$scope.rowIndex = idx;
var selected = $scope.gridOptions.selectedItems;
selected.splice(0, selected.length);
if (idx >= 0 && idx < $scope.messages.length) {
$scope.row = $scope.messages.find(function (msg) { return msg[key] === jmsMessageID; });
if ($scope.row) {
selected.push($scope.row);
}
}
else {
$scope.row = null;
}
}
ARTEMIS.selectCurrentMessage = selectCurrentMessage;
/**
* - Adds functions needed for message browsing with details
* - Adds a watch to deselect all rows after closing the slideout with message details
* TODO: export these functions too?
*
* @param $scope
*/
function decorate($scope) {
$scope.selectRowIndex = function (idx) {
$scope.rowIndex = idx;
var selected = $scope.gridOptions.selectedItems;
selected.splice(0, selected.length);
if (idx >= 0 && idx < $scope.messages.length) {
$scope.row = $scope.messages[idx];
if ($scope.row) {
selected.push($scope.row);
}
}
else {
$scope.row = null;
}
};
$scope.$watch("showMessageDetails", function () {
if (!$scope.showMessageDetails) {
$scope.row = null;
$scope.gridOptions.selectedItems.splice(0, $scope.gridOptions.selectedItems.length);
}
});
}
ARTEMIS.decorate = decorate;
})(ARTEMIS || (ARTEMIS = {}));

View File

@ -0,0 +1,246 @@
/*
* 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.
*/
/**
* @module ARTEMIS
* @main ARTEMIS
*
* The main entrypoint for the ARTEMIS module
*
*/
var ARTEMIS = (function(ARTEMIS) {
/**
* @property pluginName
* @type {string}
*
* The name of this plugin
*/
ARTEMIS.pluginName = "ARTEMIS";
/**
* @property log
* @type {Logging.Logger}
*
* This plugin's logger instance
*/
ARTEMIS.log = Logger.get(ARTEMIS.pluginName);
/**
* @property templatePath
* @type {string}
*
* The top level path to this plugin's partials
*/
ARTEMIS.templatePath = "../artemis-plugin/plugin/html/";
/**
* @property jmxDomain
* @type {string}
*
* The JMX domain this plugin mostly works with
*/
ARTEMIS.jmxDomain = "hawtio"
/**
* @property mbeanType
* @type {string}
*
* The mbean type this plugin will work with
*/
ARTEMIS.mbeanType = "ARTEMISHandler";
/**
* @property mbean
* @type {string}
*
* The mbean's full object name
*/
ARTEMIS.mbean = ARTEMIS.jmxDomain + ":type=" + ARTEMIS.mbeanType;
/**
* @property SETTINGS_KEY
* @type {string}
*
* The key used to fetch our settings from local storage
*/
ARTEMIS.SETTINGS_KEY = 'ARTEMISSettings';
/**
* @property module
* @type {object}
*
* This plugin's angularjs module instance
*/
ARTEMIS.module = angular.module(ARTEMIS.pluginName, ['bootstrap', 'ngResource', 'ui.bootstrap.dialog', 'hawtioCore', 'camel', 'hawtio-ui']);
// set up the routing for this plugin, these are referenced by the subleveltabs added below
ARTEMIS.module.config(function($routeProvider) {
$routeProvider
.when('/artemis/createAddress', {
templateUrl: ARTEMIS.templatePath + 'createAddress.html'
})
.when('/artemis/deleteAddress', {
templateUrl: ARTEMIS.templatePath + 'deleteAddress.html'
})
.when('/artemis/deleteQueue', {
templateUrl: ARTEMIS.templatePath + 'deleteQueue.html'
})
.when('/artemis/createQueue', {
templateUrl: ARTEMIS.templatePath + 'createQueue.html'
})
.when('/artemis/browseQueue', {
templateUrl: ARTEMIS.templatePath + 'browseQueue.html'
})
.when('/artemis/diagram', {
templateUrl: ARTEMIS.templatePath + 'brokerDiagram.html'
})
.when('/artemis/sendMessage', {
templateUrl: ARTEMIS.templatePath + 'sendMessage.html'
});
});
ARTEMIS.module.factory('artemisMessage', function () {
return { 'message': null };
});
// one-time initialization happens in the run function
// of our module
ARTEMIS.module.run(function(workspace, viewRegistry, helpRegistry, preferencesRegistry, localStorage, jolokia, ARTEMISService, $rootScope) {
// let folks know we're actually running
ARTEMIS.log.info("plugin running " + jolokia);
var artemisJmxDomain = localStorage['artemisJmxDomain'] || "org.apache.activemq.artemis";
ARTEMISService.initArtemis();
// tell hawtio that we have our own custom layout for
// our view
viewRegistry["artemis"] = ARTEMIS.templatePath + "artemisLayout.html";
helpRegistry.addUserDoc("artemis", "../artemis-plugin/plugin/doc/help.md", function () {
return workspace.treeContainsDomainAndProperties(artemisJmxDomain);
});
preferencesRegistry.addTab("Artemis", ARTEMIS.templatePath + "preferences.html", function () {
return workspace.treeContainsDomainAndProperties(artemisJmxDomain);
});
// Add a top level tab to hawtio's navigation bar
workspace.topLevelTabs.push({
id: "artemis",
content: "Artemis",
title: "Artemis Broker",
isValid: function (workspace) {
return workspace.treeContainsDomainAndProperties(artemisJmxDomain);
},
href: function () {
return "#/jmx/attributes?tab=artemis";
},
isActive: function () {
return workspace.isLinkActive("artemis");
}
});
workspace.subLevelTabs.push({
content: '<i class="icon-plus"></i> Create',
title: "Create a new address",
isValid: function (workspace) {
return isBroker(workspace, artemisJmxDomain) || isAddressFolder(workspace, artemisJmxDomain);
},
href: function () {
return "#/artemis/createAddress";
}
});
workspace.subLevelTabs.push({
content: '<i class="icon-plus"></i> Delete',
title: "Delete an address",
isValid: function (workspace) {
return isAddress(workspace, artemisJmxDomain);
},
href: function () {
return "#/artemis/deleteAddress";
}
});
workspace.subLevelTabs.push({
content: '<i class="icon-plus"></i> Create',
title: "Create a new queue",
isValid: function (workspace) {
return isAddress(workspace, artemisJmxDomain)
},
href: function () {
return "#/artemis/createQueue"
}
});
workspace.subLevelTabs.push({
content: '<i class="icon-remove"></i> Delete',
title: "Delete or purge this queue",
isValid: function (workspace) {
return isQueue(workspace, artemisJmxDomain)
},
href: function () {
return "#/artemis/deleteQueue"
}
});
workspace.subLevelTabs.push({
content: '<i class="icon-envelope"></i> Browse',
title: "Browse the messages on the queue",
isValid: function (workspace) { return isQueue(workspace, artemisJmxDomain); },
href: function () { return "#/artemis/browseQueue"; }
});
workspace.subLevelTabs.push({
content: '<i class="icon-pencil"></i> Send',
title: "Send a message to this address",
isValid: function (workspace) { return isAddress(workspace, artemisJmxDomain) || isQueue(workspace, artemisJmxDomain); },
href: function () { return "#/artemis/sendMessage"; }
});
workspace.subLevelTabs.push({
content: '<i class="icon-picture"></i> Diagram',
title: "View a diagram of the producers, destinations and consumers",
isValid: function (workspace) { return workspace.isTopTabActive("artemis") || workspace.selectionHasDomain(artemisJmxDomain); },
href: function () { return "#/artemis/diagram"; }
});
});
function isBroker(workspace, domain) {
return workspace.hasDomainAndProperties(domain, {'broker': 'Broker'}, 3);
}
function isAddressFolder(workspace, domain) {
return workspace.selectionHasDomainAndLastFolderName(domain, 'addresses');
}
function isAddress(workspace, domain) {
return workspace.hasDomainAndProperties(domain, {'component': 'addresses'}) && !workspace.hasDomainAndProperties(domain, {'subcomponent': 'queues'});
}
function isQueue(workspace, domain) {
return workspace.hasDomainAndProperties(domain, {'subcomponent': 'queues'});
}
return ARTEMIS;
}(ARTEMIS || {}));
// Very important! Add our module to hawtioPluginLoader so it
// bootstraps our module
hawtioPluginLoader.addModule(ARTEMIS.pluginName);

View File

@ -0,0 +1,44 @@
/*
* 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.
*/
/**
* @module ARTEMIS
*/
var ARTEMIS = (function(ARTEMIS) {
ARTEMIS.SERVER = 'Server Messages';
// The ARTEMIS service handles the connection to
// the Artemis Jolokia server in the background
ARTEMIS.module.factory("ARTEMISService", function(jolokia, $rootScope) {
var self = {
artemisConsole: undefined,
getVersion: function(jolokia) {
ARTEMIS.log.info("Connecting to ARTEMIS service: " + self.artemisConsole.getServerAttributes(jolokia));
} ,
initArtemis: function(broker) {
ARTEMIS.log.info("*************creating Artemis Console************");
self.artemisConsole = new ArtemisConsole();
}
};
return self;
});
return ARTEMIS;
}(ARTEMIS || {}));

View File

@ -0,0 +1,665 @@
/*
* 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.
*/
/**
* @module ARTEMIS
*/
var ARTEMIS = (function(ARTEMIS) {
ARTEMIS.BrokerDiagramController = function ($scope, $compile, $location, localStorage, ARTEMISService, jolokia, workspace, $routeParams) {
Fabric.initScope($scope, $location, jolokia, workspace);
var artemisJmxDomain = localStorage['artemisJmxDomain'] || "org.apache.activemq.artemis";
$scope.selectedNode = null;
var defaultFlags = {
panel: true,
popup: false,
label: true,
group: false,
profile: false,
slave: false,
broker: true,
network: true,
container: false,
address: true,
queue: true,
consumer: true,
producer: true
};
$scope.viewSettings = {};
$scope.shapeSize = {
broker: 20,
queue: 14,
address: 14
};
var redrawGraph = Core.throttled(doRedrawGraph, 1000);
var graphBuilder = new ForceGraph.GraphBuilder();
Core.bindModelToSearchParam($scope, $location, "searchFilter", "q", "");
angular.forEach(defaultFlags, function (defaultValue, key) {
var modelName = "viewSettings." + key;
// bind model values to search params...
function currentValue() {
var answer = $location.search()[paramName] || defaultValue;
return answer === "false" ? false : answer;
}
var paramName = key;
var value = currentValue();
Core.pathSet($scope, modelName, value);
$scope.$watch(modelName, function () {
var current = Core.pathGet($scope, modelName);
var old = currentValue();
if (current !== old) {
var defaultValue = defaultFlags[key];
if (current !== defaultValue) {
if (!current) {
current = "false";
}
$location.search(paramName, current);
}
else {
$location.search(paramName, null);
}
}
redrawGraph();
});
});
$scope.connectToBroker = function () {
var selectedNode = $scope.selectedNode;
if (selectedNode) {
var container = selectedNode["brokerContainer"] || selectedNode;
connectToBroker(container, selectedNode["brokerName"]);
}
};
function connectToBroker(container, brokerName, postfix) {
if (postfix === void 0) {
postfix = null;
}
var view = "/jmx/attributes?tab=artemis";
if (!postfix) {
if (brokerName) {
// lets default to the broker view
postfix = "nid=root-" + artemisJmxDomain + "-Broker-" + brokerName;
}
}
if (postfix) {
view += "&" + postfix;
}
var path = Core.url("/#" + view);
window.open(path, '_destination');
window.focus();
}
$scope.connectToDestination = function () {
var selectedNode = $scope.selectedNode;
if (selectedNode) {
var container = selectedNode["brokerContainer"] || selectedNode;
var brokerName = selectedNode["brokerName"];
var destinationType = selectedNode["destinationType"] || selectedNode["typeLabel"];
var destinationName = selectedNode["destinationName"];
var postfix = null;
if (brokerName && destinationType && destinationName) {
postfix = "nid=root-" + artemisJmxDomain + "-Broker-" + brokerName + "-" + destinationType + "-" + destinationName;
}
connectToBroker(container, brokerName, postfix);
}
};
$scope.$on('$destroy', function (event) {
stopOldJolokia();
});
function stopOldJolokia() {
var oldJolokia = $scope.selectedNodeJolokia;
if (oldJolokia && oldJolokia !== jolokia) {
oldJolokia.stop();
}
}
$scope.$watch("selectedNode", function (newValue, oldValue) {
// lets cancel any previously registered thingy
if ($scope.unregisterFn) {
$scope.unregisterFn();
$scope.unregisterFn = null;
}
var node = $scope.selectedNode;
if (node) {
var mbean = node.objectName;
var brokerContainer = node.brokerContainer || {};
var nodeJolokia = node.jolokia || brokerContainer.jolokia || jolokia;
if (nodeJolokia !== $scope.selectedNodeJolokia) {
stopOldJolokia();
$scope.selectedNodeJolokia = nodeJolokia;
if (nodeJolokia !== jolokia) {
var rate = Core.parseIntValue(localStorage['updateRate'] || "2000", "update rate");
if (rate) {
nodeJolokia.start(rate);
}
}
}
var dummyResponse = {value: node.panelProperties || {}};
if (mbean && nodeJolokia) {
ARTEMIS.log.debug("reading ", mbean, " on remote container");
$scope.unregisterFn = Core.register(nodeJolokia, $scope, {
type: 'read',
mbean: mbean
}, onSuccess(renderNodeAttributes, {
error: function (response) {
// probably we've got a wrong mbean name?
// so lets render at least
renderNodeAttributes(dummyResponse);
Core.defaultJolokiaErrorHandler(response);
}
}));
}
else {
ARTEMIS.log.debug("no mbean or jolokia available, using dummy response");
renderNodeAttributes(dummyResponse);
}
}
});
function getDestinationTypeName(attributes) {
var prefix = attributes["DestinationTemporary"] ? "Temporary " : "";
return prefix + (attributes["DestinationTopic"] ? "Topic" : "Queue");
}
var ignoreNodeAttributes = ["Broker", "BrokerId", "BrokerName", "Connection", "DestinationName", "DestinationQueue", "DestinationTemporary", "DestinationTopic",];
var ignoreNodeAttributesByType = {
producer: ["Producer", "ProducerId"],
queue: ["Name", "MessageGroups", "MessageGroupType", "Subscriptions"],
topic: ["Name", "Subscriptions"],
broker: ["DataDirectory", "DurableTopicSubscriptions", "DynamicDestinationProducers", "InactiveDurableToppicSubscribers"]
};
var brokerShowProperties = ["Version", "Started"];
var onlyShowAttributesByType = {
broker: brokerShowProperties,
brokerSlave: brokerShowProperties
};
function renderNodeAttributes(response) {
var properties = [];
if (response) {
var value = response.value || {};
$scope.selectedNodeAttributes = value;
var selectedNode = $scope.selectedNode || {};
var brokerContainer = selectedNode['brokerContainer'] || {};
var nodeType = selectedNode["type"];
var brokerName = selectedNode["brokerName"];
var containerId = selectedNode["container"] || brokerContainer["container"];
var group = selectedNode["group"] || brokerContainer["group"];
var jolokiaUrl = selectedNode["jolokiaUrl"] || brokerContainer["jolokiaUrl"];
var profile = selectedNode["profile"] || brokerContainer["profile"];
var version = selectedNode["version"] || brokerContainer["version"];
var isBroker = nodeType && nodeType.startsWith("broker");
var ignoreKeys = ignoreNodeAttributes.concat(ignoreNodeAttributesByType[nodeType] || []);
var onlyShowKeys = onlyShowAttributesByType[nodeType];
angular.forEach(value, function (v, k) {
if (onlyShowKeys ? onlyShowKeys.indexOf(k) >= 0 : ignoreKeys.indexOf(k) < 0) {
var formattedValue = Core.humanizeValueHtml(v);
properties.push({key: Core.humanizeValue(k), value: formattedValue});
}
});
properties = properties.sortBy("key");
var brokerProperty = null;
if (brokerName) {
var brokerHtml = '<a target="broker" ng-click="connectToBroker()">' + '<img title="Apache Artemis" src="img/icons/messagebroker.svg"> ' + brokerName + '</a>';
if (version && profile) {
var brokerLink = Fabric.brokerConfigLink(workspace, jolokia, localStorage, version, profile, brokerName);
if (brokerLink) {
brokerHtml += ' <a title="configuration settings" target="brokerConfig" href="' + brokerLink + '"><i class="icon-tasks"></i></a>';
}
}
var html = $compile(brokerHtml)($scope);
brokerProperty = {key: "Broker", value: html};
if (!isBroker) {
properties.splice(0, 0, brokerProperty);
}
}
if (containerId) {
//var containerModel = "selectedNode" + (selectedNode['brokerContainer'] ? ".brokerContainer" : "");
properties.splice(0, 0, {
key: "Container",
value: $compile('<div fabric-container-link="' + selectedNode['container'] + '"></div>')($scope)
});
}
var destinationName = value["DestinationName"] || selectedNode["destinationName"];
if (destinationName && (nodeType !== "queue" && nodeType !== "topic")) {
var destinationTypeName = getDestinationTypeName(value);
var html = createDestinationLink(destinationName, destinationTypeName);
properties.splice(0, 0, {key: destinationTypeName, value: html});
}
var typeLabel = selectedNode["typeLabel"];
var name = selectedNode["name"] || selectedNode["id"] || selectedNode['objectName'];
if (typeLabel) {
var html = name;
if (nodeType === "queue" || nodeType === "topic") {
html = createDestinationLink(name, nodeType);
}
var typeProperty = {key: typeLabel, value: html};
if (isBroker && brokerProperty) {
typeProperty = brokerProperty;
}
properties.splice(0, 0, typeProperty);
}
}
$scope.selectedNodeProperties = properties;
Core.$apply($scope);
}
/**
* Generates the HTML for a link to the destination
*/
function createDestinationLink(destinationName, destinationType) {
if (destinationType === void 0) {
destinationType = "queue";
}
return $compile('<a target="destination" title="' + destinationName + '" ng-click="connectToDestination()">' + destinationName + '</a>')($scope);
}
$scope.$watch("searchFilter", function (newValue, oldValue) {
redrawGraph();
});
// lets just use the current stuff from the workspace
$scope.$watch('workspace.tree', function () {
redrawGraph();
});
$scope.$on('jmxTreeUpdated', function () {
redrawGraph();
});
function onBrokerData(response) {
if (response) {
var responseJson = angular.toJson(response.value);
if ($scope.responseJson === responseJson) {
return;
}
$scope.responseJson = responseJson;
$scope.brokers = response.value;
doRedrawGraph();
}
}
function redrawLocalBroker() {
var container = {
jolokia: jolokia
};
var containerId = "local";
$scope.activeContainers = {
containerId: container
};
var brokers = [];
jolokia.search(artemisJmxDomain + ":broker=*", onSuccess(function (response) {
angular.forEach(response, function (objectName) {
var atts = ARTEMISService.artemisConsole.getServerAttributes(jolokia, objectName);
var val = atts.value;
var details = Core.parseMBean(objectName);
if (details) {
var properties = details['attributes'];
ARTEMIS.log.info("Got broker: " + objectName + " on container: " + containerId + " properties: " + angular.toJson(properties, true));
if (properties) {
var master = true;
var brokerId = properties["broker"] || "unknown";
var nodeId = val["NodeID"];
var theBroker = {
brokerId: brokerId,
nodeId: nodeId
};
brokers.push(theBroker);
if ($scope.viewSettings.broker) {
var broker = getOrAddBroker(master, brokerId, nodeId, containerId, container, properties);
}
}
}
});
redrawActiveContainers(brokers);
}));
}
function redrawActiveContainers(brokers) {
// TODO delete any nodes from dead containers in containersToDelete
angular.forEach($scope.activeContainers, function (container, id) {
var containerJolokia = container.jolokia;
if (containerJolokia) {
onContainerJolokia(containerJolokia, container, id, brokers);
}
else {
Fabric.containerJolokia(jolokia, id, function (containerJolokia) {
return onContainerJolokia(containerJolokia, container, id, brokers);
});
}
});
$scope.graph = graphBuilder.buildGraph();
Core.$apply($scope);
}
function doRedrawGraph() {
graphBuilder = new ForceGraph.GraphBuilder();
redrawLocalBroker();
}
function brokerNameMarkup(brokerName) {
return brokerName ? "<p></p>broker: " + brokerName + "</p>" : "";
}
function onContainerJolokia(containerJolokia, container, id, brokers) {
function createQueues(brokers) {
if ($scope.viewSettings.queue) {
containerJolokia.search(artemisJmxDomain + ":*,subcomponent=queues", onSuccess(function (response) {
angular.forEach(response, function (objectName) {
var details = Core.parseMBean(objectName);
if (details) {
var properties = details['attributes'];
if (properties) {
configureDestinationProperties(properties);
var brokerName = properties.broker;
var addressName = properties.address;
var typeName = "queue";
var queueName = properties.queue;
var routingType = properties["routing-type"];
var destination = getOrAddQueue(properties, typeName, routingType, queueName, addressName, brokerName);
}
}
});
graphModelUpdated();
createConsumersAndNetwork(brokers);
}));
} else {
createConsumersAndNetwork(brokers);
}
}
function createAddresses(brokers) {
if ($scope.viewSettings.address) {
containerJolokia.search(artemisJmxDomain + ":*,component=addresses", onSuccess(function (response) {
angular.forEach(response, function (objectName) {
var details = Core.parseMBean(objectName);
if (details) {
var properties = details['attributes'];
if (properties) {
var brokerName = properties.broker;
var typeName = "address";
var addressName = properties.address;
var destination = getOrAddAddress(properties, typeName, addressName, brokerName);
}
}
});
createQueues(brokers);
graphModelUpdated();
}));
} else {
createQueues(brokers);
}
}
function createConsumersAndNetwork(brokers) {
angular.forEach(brokers, function (broker) {
mBean = artemisJmxDomain + ":broker=" + broker.brokerId;
// find consumers
if ($scope.viewSettings.consumer) {
ARTEMISService.artemisConsole.getConsumers(mBean, containerJolokia, onSuccess(function (properties) {
consumers = properties.value;
ARTEMIS.log.info(consumers);
angular.forEach(angular.fromJson(consumers), function (consumer) {
if (consumer) {
configureDestinationProperties(consumer);
var consumerId = consumer.sessionID + "-" + consumer.consumerID;
if (consumerId) {
var queueName = consumer.queueName;
var consumerNode = getOrAddNode("consumer", consumerId, consumer, function () {
return {
typeLabel: "Consumer",
brokerContainer: container,
//objectName: "null",
jolokia: containerJolokia,
popup: {
title: "Consumer: " + consumerId,
content: "<p>client: " + (consumer.connectionID || "") + "</p> " + brokerNameMarkup(broker.brokerId)
}
};
});
addLinkIds("queue:\"" + queueName + "\"", consumerNode["id"], "consumer");
}
}
});
graphModelUpdated();
}));
}
// find networks of brokers
if ($scope.viewSettings.network && $scope.viewSettings.broker) {
ARTEMISService.artemisConsole.getRemoteBrokers(mBean, containerJolokia, onSuccess(function (properties) {
remoteBrokers = properties.value;
ARTEMIS.log.info("remoteBrokers=" + angular.toJson(remoteBrokers))
angular.forEach(angular.fromJson(remoteBrokers), function (remoteBroker) {
if (remoteBroker) {
ARTEMIS.log.info("remote=" + angular.toJson(remoteBroker))
if (broker.nodeId != remoteBroker.nodeID) {
getOrAddBroker(true, "\"" + remoteBroker.live + "\"", remoteBroker.nodeID, "remote", null, properties);
addLinkIds("broker:" + broker.brokerId, "broker:" + "\"" + remoteBroker.live + "\"", "network");
var backup = remoteBroker.backup;
if (backup) {
getOrAddBroker(false, "\"" + backup + "\"", remoteBroker.nodeID, "remote", null, properties);
addLinkIds("broker:" + "\"" + remoteBroker.live + "\"", "broker:" + "\"" + backup + "\"", "network");
}
}
else {
var backup = remoteBroker.backup;
if (backup) {
getOrAddBroker(false, "\"" + remoteBroker.backup + "\"", remoteBroker.nodeID, "remote", null, properties);
addLinkIds("broker:" + broker.brokerId, "broker:" + "\"" + remoteBroker.backup + "\"", "network");
}
}
}
});
graphModelUpdated();
}));
}
});
}
if (containerJolokia) {
container.jolokia = containerJolokia;
function getOrAddQueue(properties, typeName, routingType, queueName, addressName, brokerName) {
var queue = getOrAddNode(typeName.toLowerCase(), queueName, properties, function () {
var objectName = "";
if (addressName) {
objectName = artemisJmxDomain + ":broker=" + brokerName + ",component=addresses,address=" + addressName + ",subcomponent=queues,routing-type=" + routingType + ",queue=" + queueName;
}
ARTEMIS.log.info(objectName);
var answer = {
typeLabel: typeName,
brokerContainer: container,
objectName: objectName,
jolokia: containerJolokia,
popup: {
title: "queue: " + queueName,
content: "address:" + addressName
}
};
if (!addressName) {
containerJolokia.search(artemisJmxDomain + ":broker=" + brokerName + ",component=addresses,address=" + addressName + ",subcomponent=queues,routing-type=" + routingType + ",queue=" + queueName + ",*", onSuccess(function (response) {
if (response && response.length) {
answer.objectName = response[0];
}
}));
}
return answer;
});
if (queue && $scope.viewSettings.broker && addressName) {
addLinkIds("address:" + addressName, queue["id"], "queue");
}
return queue;
}
function getOrAddAddress(properties, typeName, destinationName, brokerName) {
var destination = getOrAddNode(typeName.toLowerCase(), destinationName, properties, function () {
var objectName = "";
if (brokerName) {
objectName = artemisJmxDomain + ":broker=" + brokerName + ",component=addresses,address=" + destinationName;
}
var answer = {
typeLabel: typeName,
brokerContainer: container,
objectName: objectName,
jolokia: containerJolokia,
popup: {
title: typeName + ": " + destinationName,
content: brokerNameMarkup(brokerName)
}
};
if (!brokerName) {
containerJolokia.search(artemisJmxDomain + ":broker=" + brokerName + ",component=addresses,address=" + destinationName + ",*", onSuccess(function (response) {
if (response && response.length) {
answer.objectName = response[0];
}
}));
}
return answer;
});
if (destination && $scope.viewSettings.broker && brokerName) {
addLinkIds(brokerNodeId(brokerName), destination["id"], "address");
}
return destination;
}
createAddresses(brokers);
}
}
function graphModelUpdated() {
$scope.graph = graphBuilder.buildGraph();
Core.$apply($scope);
}
function getOrAddBroker(master, brokerId, nodeId, containerId, container, brokerStatus) {
var broker = null;
var brokerFlag = master ? $scope.viewSettings.broker : $scope.viewSettings.slave;
if (brokerFlag) {
broker = getOrAddNode("broker", brokerId, brokerStatus, function () {
return {
type: master ? "broker" : "brokerSlave",
typeLabel: master ? "Broker" : "Slave Broker",
popup: {
title: (master ? "Master" : "Slave") + " Broker: " + brokerId,
content: "<p>Container: " + containerId + "</p> Node ID: " + nodeId
}
};
});
if (!broker['objectName']) {
// lets try guess the mbean name
broker['objectName'] = artemisJmxDomain + ":broker=" + brokerId;
ARTEMIS.log.debug("Guessed broker mbean: " + broker['objectName']);
}
if (!broker['brokerContainer'] && container) {
broker['brokerContainer'] = container;
}
if (!broker['nodeID']) {
broker['nodeID'] = nodeId;
}
}
return broker;
}
function getOrAddNode(typeName, id, properties, createFn) {
var node = null;
if (id) {
var nodeId = typeName + ":" + id;
node = graphBuilder.getNode(nodeId);
if (!node) {
var nodeValues = createFn();
node = angular.copy(properties);
angular.forEach(nodeValues, function (value, key) {
return node[key] = value;
});
node['id'] = nodeId;
if (!node['type']) {
node['type'] = typeName;
}
if (!node['name']) {
node['name'] = id;
}
if (node) {
var size = $scope.shapeSize[typeName];
if (size && !node['size']) {
node['size'] = size;
}
if (!node['summary']) {
node['summary'] = node['popup'] || "";
}
if (!$scope.viewSettings.popup) {
delete node['popup'];
}
if (!$scope.viewSettings.label) {
delete node['name'];
}
// lets not add nodes which are defined as being disabled
var enabled = $scope.viewSettings[typeName];
if (enabled || !angular.isDefined(enabled)) {
graphBuilder.addNode(node);
}
else {
}
}
}
}
return node;
}
function addLink(object1, object2, linkType) {
if (object1 && object2) {
addLinkIds(object1.id, object2.id, linkType);
}
}
function addLinkIds(id1, id2, linkType) {
ARTEMIS.log.info("adding " + id1 + " to " + id2 + " " + linkType)
if (id1 && id2) {
graphBuilder.addLink(id1, id2, linkType);
}
}
function brokerNodeId(brokerId) {
return brokerId ? "broker:" + brokerId : null;
}
/**
* Avoid the JMX type property clashing with the ForceGraph type property; used for associating css classes with nodes on the graph
*
* @param properties
*/
function renameTypeProperty(properties) {
properties.mbeanType = properties['type'];
delete properties['type'];
}
function configureDestinationProperties(properties) {
renameTypeProperty(properties);
var destinationType = properties.destinationType || "Queue";
var typeName = destinationType.toLowerCase();
properties.isQueue = !typeName.startsWith("t");
properties['destType'] = typeName;
}
};
return ARTEMIS;
} (ARTEMIS || {}));

View File

@ -0,0 +1,499 @@
/*
* 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.
*/
/**
* @module ARTEMIS
*/
var ARTEMIS = (function(ARTEMIS) {
ARTEMIS.BrowseQueueController = function ($scope, workspace, ARTEMISService, jolokia, localStorage, artemisMessage, $location, $timeout) {
$scope.searchText = '';
$scope.allMessages = [];
$scope.messages = [];
$scope.headers = {};
$scope.mode = 'text';
$scope.deleteDialog = false;
$scope.moveDialog = false;
$scope.gridOptions = {
selectedItems: [],
data: 'messages',
displayFooter: false,
showFilter: false,
showColumnMenu: true,
enableColumnResize: true,
enableColumnReordering: true,
enableHighlighting: true,
filterOptions: {
filterText: '',
useExternalFilter: true
},
selectWithCheckboxOnly: true,
showSelectionCheckbox: true,
maintainColumnRatios: false,
columnDefs: [
{
field: 'messageID',
displayName: 'Message ID',
cellTemplate: '<div class="ngCellText"><a ng-click="openMessageDialog(row)">{{row.entity.messageID}}</a></div>',
// for ng-grid
width: '10%'
},
{
field: 'userID',
displayName: 'User ID',
width: '10%'
},
{
field: 'type',
displayName: 'Type',
width: '10%'
},
{
field: 'durable',
displayName: 'Durable',
width: '10%'
},
{
field: 'priority',
displayName: 'Priority',
width: '7%'
},
{
field: 'timestamp',
displayName: 'Timestamp',
width: '19%'
},
{
field: 'expiration',
displayName: 'Expires',
width: '10%'
},
{
field: 'redelivered',
displayName: 'Redelivered',
width: '10%'
}
],
afterSelectionChange: afterSelectionChange
};
$scope.showMessageDetails = false;
var ignoreColumns = ["PropertiesText", "BodyPreview", "text"];
var flattenColumns = ["BooleanProperties", "ByteProperties", "ShortProperties", "IntProperties", "LongProperties", "FloatProperties", "DoubleProperties", "StringProperties"];
$scope.$watch('workspace.selection', function () {
if (workspace.moveIfViewInvalid()) {
return;
}
// lets defer execution as we may not have the selection just yet
setTimeout(loadTable, 50);
});
$scope.$watch('gridOptions.filterOptions.filterText', function (filterText) {
filterMessages(filterText);
});
$scope.openMessageDialog = function (message) {
ARTEMIS.selectCurrentMessage(message, "messageID", $scope);
if ($scope.row) {
$scope.mode = CodeEditor.detectTextFormat($scope.row.Text);
$scope.showMessageDetails = true;
}
};
$scope.refresh = loadTable;
ARTEMIS.decorate($scope);
$scope.moveMessages = function () {
var selection = workspace.selection;
var mbean = selection.objectName;
if (mbean && selection) {
var selectedItems = $scope.gridOptions.selectedItems;
$scope.message = "Moved " + Core.maybePlural(selectedItems.length, "message" + " to " + $scope.queueName);
var operation = "moveMessageTo(java.lang.String, java.lang.String)";
angular.forEach(selectedItems, function (item, idx) {
var id = item.messageID;
if (id) {
var callback = (idx + 1 < selectedItems.length) ? intermediateResult : moveSuccess;
jolokia.execute(mbean, operation, id, $scope.queueName, onSuccess(callback));
ARTEMISService.artemisConsole.moveMessage(mbean, jolokia, id, $scope.queueName, onSuccess(callback));
}
});
}
};
$scope.resendMessage = function () {
var selection = workspace.selection;
var mbean = selection.objectName;
if (mbean && selection) {
var selectedItems = $scope.gridOptions.selectedItems;
//always assume a single message
artemisMessage.message = selectedItems[0];
$location.path('artemis/sendMessage');
}
};
$scope.deleteMessages = function () {
var selection = workspace.selection;
var mbean = selection.objectName;
if (mbean && selection) {
var selectedItems = $scope.gridOptions.selectedItems;
$scope.message = "Deleted " + Core.maybePlural(selectedItems.length, "message");
angular.forEach(selectedItems, function (item, idx) {
var id = item.messageID;
if (id) {
var callback = (idx + 1 < selectedItems.length) ? intermediateResult : operationSuccess;
ARTEMISService.artemisConsole.deleteMessage(mbean, jolokia, id, onSuccess(callback));
}
});
}
};
$scope.retryMessages = function () {
var selection = workspace.selection;
var mbean = selection.objectName;
if (mbean && selection) {
var selectedItems = $scope.gridOptions.selectedItems;
$scope.message = "Retry " + Core.maybePlural(selectedItems.length, "message");
var operation = "retryMessage(java.lang.String)";
angular.forEach(selectedItems, function (item, idx) {
var id = item.messageID;
if (id) {
var callback = (idx + 1 < selectedItems.length) ? intermediateResult : operationSuccess;
jolokia.execute(mbean, operation, id, onSuccess(callback));
ARTEMISService.artemisConsole.retryMessage(mbean, jolokia, id, onSuccess(callback));
}
});
}
};
$scope.queueNames = function (completionText) {
var queuesFolder = ARTEMIS.getSelectionQueuesFolder(workspace);
if (queuesFolder) {
var selectedQueue = workspace.selection.key;
var otherQueues = queuesFolder.children.exclude(function (child) {
return child.key == selectedQueue;
});
return (otherQueues) ? otherQueues.map(function (n) {
return n.title;
}) : [];
}
else {
return [];
}
};
function populateTable(response) {
var data = response.value;
ARTEMIS.log.info("loading data:" + data);
if (!angular.isArray(data)) {
$scope.allMessages = [];
angular.forEach(data, function (value, idx) {
$scope.allMessages.push(value);
});
}
else {
$scope.allMessages = data;
}
angular.forEach($scope.allMessages, function (message) {
message.headerHtml = createHeaderHtml(message);
message.bodyText = createBodyText(message);
});
filterMessages($scope.gridOptions.filterOptions.filterText);
Core.$apply($scope);
}
/*
* For some reason using ng-repeat in the modal dialog doesn't work so lets
* just create the HTML in code :)
*/
function createBodyText(message) {
ARTEMIS.log.info("loading message:" + message);
if (message.text) {
var body = message.text;
var lenTxt = "" + body.length;
message.textMode = "text (" + lenTxt + " chars)";
return body;
}
else if (message.BodyPreview) {
var code = Core.parseIntValue(localStorage["ARTEMISBrowseBytesMessages"] || "1", "browse bytes messages");
var body;
message.textMode = "bytes (turned off)";
if (code != 99) {
var bytesArr = [];
var textArr = [];
message.BodyPreview.forEach(function (b) {
if (code === 1 || code === 2) {
// text
textArr.push(String.fromCharCode(b));
}
if (code === 1 || code === 4) {
// hex and must be 2 digit so they space out evenly
var s = b.toString(16);
if (s.length === 1) {
s = "0" + s;
}
bytesArr.push(s);
}
else {
// just show as is without spacing out, as that is usually more used for hex than decimal
var s = b.toString(10);
bytesArr.push(s);
}
});
var bytesData = bytesArr.join(" ");
var textData = textArr.join("");
if (code === 1 || code === 2) {
// bytes and text
var len = message.BodyPreview.length;
var lenTxt = "" + textArr.length;
body = "bytes:\n" + bytesData + "\n\ntext:\n" + textData;
message.textMode = "bytes (" + len + " bytes) and text (" + lenTxt + " chars)";
}
else {
// bytes only
var len = message.BodyPreview.length;
body = bytesData;
message.textMode = "bytes (" + len + " bytes)";
}
}
return body;
}
else {
message.textMode = "unsupported";
return "Unsupported message body type which cannot be displayed by hawtio";
}
}
/*
* For some reason using ng-repeat in the modal dialog doesn't work so lets
* just create the HTML in code :)
*/
function createHeaderHtml(message) {
var headers = createHeaders(message);
var properties = createProperties(message);
var headerKeys = Object.extended(headers).keys();
function sort(a, b) {
if (a > b)
return 1;
if (a < b)
return -1;
return 0;
}
var propertiesKeys = Object.extended(properties).keys().sort(sort);
var jmsHeaders = headerKeys.filter(function (key) {
return key.startsWith("JMS");
}).sort(sort);
var remaining = headerKeys.subtract(jmsHeaders, propertiesKeys).sort(sort);
var buffer = [];
function appendHeader(key) {
var value = headers[key];
if (value === null) {
value = '';
}
buffer.push('<tr><td class="propertyName"><span class="green">Header</span> - ' + key + '</td><td class="property-value">' + value + '</td></tr>');
}
function appendProperty(key) {
var value = properties[key];
if (value === null) {
value = '';
}
buffer.push('<tr><td class="propertyName">' + key + '</td><td class="property-value">' + value + '</td></tr>');
}
jmsHeaders.forEach(appendHeader);
remaining.forEach(appendHeader);
propertiesKeys.forEach(appendProperty);
return buffer.join("\n");
}
function createHeaders(row) {
var answer = {};
angular.forEach(row, function (value, key) {
if (!ignoreColumns.any(key) && !flattenColumns.any(key)) {
answer[Core.escapeHtml(key)] = Core.escapeHtml(value);
}
});
return answer;
}
function createProperties(row) {
ARTEMIS.log.debug("properties: ", row);
var answer = {};
angular.forEach(row, function (value, key) {
if (!ignoreColumns.any(key) && flattenColumns.any(key)) {
angular.forEach(value, function (v2, k2) {
answer['<span class="green">' + key.replace('Properties', ' Property') + '</span> - ' + Core.escapeHtml(k2)] = Core.escapeHtml(v2);
});
}
});
return answer;
}
function loadTable() {
ARTEMIS.log.info("loading table")
var objName;
$scope.gridOptions.selectedItems.length = 0;
if (workspace.selection) {
objName = workspace.selection.objectName;
}
else {
// in case of refresh
var key = location.search()['nid'];
var node = workspace.keyToNodeMap[key];
objName = node.objectName;
}
if (objName) {
$scope.dlq = false;
var queueName = jolokia.getAttribute(objName, "Name");
var artemisDLQ = localStorage['artemisDLQ'] || "DLQ";
var artemisExpiryQueue = localStorage['artemisExpiryQueue'] || "ExpiryQueue";
ARTEMIS.log.info("loading table" + artemisExpiryQueue);
if (queueName == artemisDLQ || queueName == artemisExpiryQueue) {
onDlq(true);
}
else {
onDlq(false);
}
ARTEMISService.artemisConsole.browse(objName, jolokia, onSuccess(populateTable));
}
}
function onDlq(response) {
ARTEMIS.log.info("onDLQ=" + response);
$scope.dlq = response;
Core.$apply($scope);
}
function intermediateResult() {
}
function operationSuccess() {
$scope.messageDialog = false;
deselectAll();
Core.notification("success", $scope.message);
loadTable();
setTimeout(loadTable, 50);
}
function moveSuccess() {
operationSuccess();
workspace.loadTree();
}
function filterMessages(filter) {
var searchConditions = buildSearchConditions(filter);
evalFilter(searchConditions);
}
function evalFilter(searchConditions) {
if (!searchConditions || searchConditions.length === 0) {
$scope.messages = $scope.allMessages;
}
else {
ARTEMIS.log.debug("Filtering conditions:", searchConditions);
$scope.messages = $scope.allMessages.filter(function (message) {
ARTEMIS.log.debug("Message:", message);
var matched = true;
$.each(searchConditions, function (index, condition) {
if (!condition.column) {
matched = matched && evalMessage(message, condition.regex);
}
else {
matched = matched && (message[condition.column] && condition.regex.test(message[condition.column])) || (message.StringProperties && message.StringProperties[condition.column] && condition.regex.test(message.StringProperties[condition.column]));
}
});
return matched;
});
}
}
function evalMessage(message, regex) {
var jmsHeaders = ['JMSDestination', 'JMSDeliveryMode', 'JMSExpiration', 'JMSPriority', 'JMSmessageID', 'JMSTimestamp', 'JMSCorrelationID', 'JMSReplyTo', 'JMSType', 'JMSRedelivered'];
for (var i = 0; i < jmsHeaders.length; i++) {
var header = jmsHeaders[i];
if (message[header] && regex.test(message[header])) {
return true;
}
}
if (message.StringProperties) {
for (var property in message.StringProperties) {
if (regex.test(message.StringProperties[property])) {
return true;
}
}
}
if (message.bodyText && regex.test(message.bodyText)) {
return true;
}
return false;
}
function getRegExp(str, modifiers) {
try {
return new RegExp(str, modifiers);
}
catch (err) {
return new RegExp(str.replace(/(\^|\$|\(|\)|<|>|\[|\]|\{|\}|\\|\||\.|\*|\+|\?)/g, '\\$1'));
}
}
function buildSearchConditions(filterText) {
var searchConditions = [];
var qStr;
if (!(qStr = $.trim(filterText))) {
return;
}
var columnFilters = qStr.split(";");
for (var i = 0; i < columnFilters.length; i++) {
var args = columnFilters[i].split(':');
if (args.length > 1) {
var columnName = $.trim(args[0]);
var columnValue = $.trim(args[1]);
if (columnName && columnValue) {
searchConditions.push({
column: columnName,
columnDisplay: columnName.replace(/\s+/g, '').toLowerCase(),
regex: getRegExp(columnValue, 'i')
});
}
}
else {
var val = $.trim(args[0]);
if (val) {
searchConditions.push({
column: '',
regex: getRegExp(val, 'i')
});
}
}
}
return searchConditions;
}
function afterSelectionChange(rowItem, checkAll) {
if (checkAll === void 0) {
// then row was clicked, not select-all checkbox
$scope.gridOptions['$gridScope'].allSelected = rowItem.config.selectedItems.length == $scope.messages.length;
}
else {
$scope.gridOptions['$gridScope'].allSelected = checkAll;
}
}
function deselectAll() {
$scope.gridOptions['$gridScope'].allSelected = false;
}
}
return ARTEMIS;
} (ARTEMIS || {}));

View File

@ -0,0 +1,62 @@
/*
* 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.
*/
var ARTEMIS;
(function (ARTEMIS) {
ARTEMIS.jmsHeaderSchema = {
definitions: {
headers: {
properties: {
JMSCorrelationID: {
type: "java.lang.String"
},
JMSDeliveryMode: {
"type": "string",
"enum": [
"PERSISTENT",
"NON_PERSISTENT"
]
},
JMSDestination: {
type: "javax.jms.Destination"
},
JMSExpiration: {
type: "long"
},
JMSPriority: {
type: "int"
},
JMSReplyTo: {
type: "javax.jms.Destination"
},
JMSType: {
type: "java.lang.String"
},
JMSXGroupId: {
type: "java.lang.String"
},
_AMQ_SCHED_DELIVERY: {
type: "java.lang.String"
}
}
},
"javax.jms.Destination": {
type: "java.lang.String"
}
}
};
})(ARTEMIS || (ARTEMIS = {}));
//# sourceMappingURL=jmsHeaderSchema.js.map

View File

@ -0,0 +1,54 @@
/*
* 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.
*/
/**
* @module ARTEMIS
*/
var ARTEMIS = (function(ARTEMIS) {
/**
* @method PreferencesController
* @param $scope
*
* Controller for the Preferences interface
*/
ARTEMIS.PreferencesController = function ($scope, localStorage, userDetails, $rootScope) {
Core.initPreferenceScope($scope, localStorage, {
'artemisUserName': {
'value': userDetails.username
},
'artemisPassword': {
'value': userDetails.password
},
'artemisDLQ': {
'value': "DLQ"
},
'artemisExpiryQueue': {
'value': "ExpiryQueue"
},
'artemisBrowseBytesMessages': {
'value': 1,
'converter': parseInt,
'formatter': function (value) {
return "" + value;
}
}
});
};
return ARTEMIS;
}(ARTEMIS || {}));

View File

@ -0,0 +1,152 @@
/*
* 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.
*/
/**
* @module ARTEMIS
*/
var ARTEMIS = (function(ARTEMIS) {
/**
* @method QueueController
* @param $scope
* @param ARTEMISService
*
* Controller for the Create interface
*/
ARTEMIS.QueueController = function ($scope, workspace, ARTEMISService, jolokia, localStorage) {
Core.initPreferenceScope($scope, localStorage, {
'durable': {
'value': true,
'converter': Core.parseBooleanValue
},
'routingType': {
'value': 0,
'converter': parseInt,
'formatter': parseInt
},
'maxConsumers': {
'value': -1,
'converter': parseInt,
'formatter': parseInt
},
'purgeWhenNoConsumers': {
'value': false,
'converter': Core.parseBooleanValue
}
});
var artemisJmxDomain = localStorage['artemisJmxDomain'] || "org.apache.activemq.artemis";
$scope.workspace = workspace;
$scope.message = "";
$scope.queueType = 'true';
$scope.deleteDialog = false;
$scope.purgeDialog = false;
$scope.$watch('workspace.selection', function () {
workspace.moveIfViewInvalid();
});
function operationSuccess() {
$scope.queueName = "";
$scope.workspace.operationCounter += 1;
Core.$apply($scope);
Core.notification("success", $scope.message);
$scope.workspace.loadTree();
}
function deleteSuccess() {
// lets set the selection to the parent
workspace.removeAndSelectParentNode();
$scope.workspace.operationCounter += 1;
Core.$apply($scope);
Core.notification("success", $scope.message);
$scope.workspace.loadTree();
}
$scope.createQueue = function (queueName, routingType, durable, filter, maxConsumers, purgeWhenNoConsumers) {
var mbean = getBrokerMBean(jolokia);
if (mbean) {
var selection = workspace.selection;
var entries = selection.entries;
var address = entries["address"];
if (address.charAt(0) === '"' && address.charAt(address.length -1) === '"')
{
address = address.substr(1,address.length -2);
}
$scope.message = "Created queue " + queueName + " durable=" + durable + " filter=" + filter + " on address " + address;
if (routingType == 0) {
ARTEMIS.log.info($scope.message);
ARTEMISService.artemisConsole.createQueue(mbean, jolokia, address, "MULTICAST", queueName, durable, filter, maxConsumers, purgeWhenNoConsumers, onSuccess(operationSuccess));
ARTEMIS.log.info("executed");
} else {
ARTEMIS.log.info($scope.message);
ARTEMISService.artemisConsole.createQueue(mbean, jolokia, address, "ANYCAST", queueName, durable, filter, maxConsumers, purgeWhenNoConsumers, onSuccess(operationSuccess));
ARTEMIS.log.info("executed");
}
}
};
$scope.deleteDestination = function (isQueue) {
var selection = workspace.selection;
var entries = selection.entries;
var mbean = getBrokerMBean(jolokia);
ARTEMIS.log.info(mbean);
if (mbean) {
if (selection && jolokia && entries) {
var domain = selection.domain;
var name = entries["Destination"] || entries["destinationName"] || selection.title;
name = name.replace(/['"]+/g, '');
ARTEMIS.log.info(name);
var operation;
if (isQueue) {
$scope.message = "Deleted queue " + name;
ARTEMISService.artemisConsole.deleteQueue(mbean, jolokia, name, onSuccess(deleteSuccess));
}
else {
$scope.message = "Deleted topic " + name;
ARTEMISService.artemisConsole.deleteTopic(mbean, jolokia, name, onSuccess(deleteSuccess));
}
}
}
};
$scope.purgeDestination = function () {
var selection = workspace.selection;
var entries = selection.entries;
var mbean = getBrokerMBean(jolokia);
if (mbean) {
if (selection && jolokia && entries) {
var name = entries["Destination"] || entries["destinationName"] || selection.title;
name = name.unescapeHTML();
var operation = "purge()";
$scope.message = "Purged queue " + name;
ARTEMISService.artemisConsole.purgeQueue(mbean, jolokia, name, onSuccess(deleteSuccess));
}
}
};
$scope.name = function () {
var selection = workspace.selection;
if (selection) {
return selection.title;
}
return null;
};
function getBrokerMBean(jolokia) {
var mbean = null;
var selection = workspace.selection;
var folderNames = selection.folderNames;
mbean = "" + folderNames[0] + ":broker=" + folderNames[1];
ARTEMIS.log.info("broker=" + mbean);
return mbean;
}
};
return ARTEMIS;
} (ARTEMIS || {}));

View File

@ -0,0 +1,211 @@
/*
* 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.
*/
/**
* @module ARTEMIS
*/
var ARTEMIS;
(function (ARTEMIS) {
var DELIVERY_PERSISTENT = "2";
ARTEMIS.SendMessageController = function($route, $scope, $element, $timeout, workspace, ARTEMISService, jolokia, localStorage, $location, artemisMessage) {
Core.initPreferenceScope($scope, localStorage, {
'durable': {
'value': true,
'converter': Core.parseBooleanValue
}
});
var log = Logger.get("ARTEMIS");
$scope.noCredentials = false;
$scope.showChoose = false;
$scope.profileFileNames = [];
$scope.profileFileNameToProfileId = {};
$scope.selectedFiles = {};
$scope.container = {};
$scope.message = "\n\n\n\n";
$scope.headers = [];
// bind model values to search params...
Core.bindModelToSearchParam($scope, $location, "tab", "subtab", "compose");
Core.bindModelToSearchParam($scope, $location, "searchText", "q", "");
// only reload the page if certain search parameters change
Core.reloadWhenParametersChange($route, $scope, $location);
$scope.checkCredentials = function () {
ARTEMIS.log.info(localStorage['artemisUserName'] + " " + localStorage['artemisPassword']);
$scope.noCredentials = (Core.isBlank(localStorage['artemisUserName']) || Core.isBlank(localStorage['artemisPassword']));
};
if ($location.path().has('artemis')) {
$scope.localStorage = localStorage;
$scope.$watch('localStorage.artemisUserName', $scope.checkCredentials);
$scope.$watch('localStorage.artemisPassword', $scope.checkCredentials);
//prefill if it's a resent
if (artemisMessage.message !== null) {
$scope.message = artemisMessage.message.bodyText;
if (artemisMessage.message.PropertiesText !== null) {
for (var p in artemisMessage.message.StringProperties) {
$scope.headers.push({name: p, value: artemisMessage.message.StringProperties[p]});
}
}
}
// always reset at the end
artemisMessage.message = null;
}
$scope.openPrefs = function () {
$location.search('pref', 'Artemis');
$scope.$emit("hawtioOpenPrefs");
};
var LANGUAGE_FORMAT_PREFERENCE = "defaultLanguageFormat";
var sourceFormat = workspace.getLocalStorage(LANGUAGE_FORMAT_PREFERENCE) || "javascript";
// TODO Remove this if possible
$scope.codeMirror = undefined;
var options = {
mode: {
name: sourceFormat
},
// Quick hack to get the codeMirror instance.
onChange: function (codeMirror) {
if (!$scope.codeMirror) {
$scope.codeMirror = codeMirror;
}
}
};
$scope.codeMirrorOptions = CodeEditor.createEditorSettings(options);
$scope.addHeader = function () {
$scope.headers.push({name: "", value: ""});
// lets set the focus to the last header
if ($element) {
$timeout(function () {
var lastHeader = $element.find("input.headerName").last();
lastHeader.focus();
}, 100);
}
};
$scope.removeHeader = function (header) {
$scope.headers = $scope.headers.remove(header);
};
$scope.defaultHeaderNames = function () {
var answer = [];
function addHeaderSchema(schema) {
angular.forEach(schema.definitions.headers.properties, function (value, name) {
answer.push(name);
});
}
if (isJmsEndpoint()) {
addHeaderSchema(ARTEMIS.jmsHeaderSchema);
}
/*if (isARTEMISEndpoint()) {
addHeaderSchema(ARTEMIS.ARTEMISHeaderSchema);
}*/
return answer;
};
/* save the sourceFormat in preferences for later
* Note, this would be controller specific preferences and not the global, overriding, preferences */
// TODO Use ng-selected="changeSourceFormat()" - Although it seemed to fire multiple times..
$scope.$watch('codeMirrorOptions.mode.name', function (newValue, oldValue) {
workspace.setLocalStorage(LANGUAGE_FORMAT_PREFERENCE, newValue);
});
var sendWorked = function () {
Core.notification("success", "Message sent!");
};
$scope.autoFormat = function () {
setTimeout(function () {
CodeEditor.autoFormatEditor($scope.codeMirror);
}, 50);
};
$scope.sendMessage = function (durable) {
var body = $scope.message;
ARTEMIS.log.info(body);
doSendMessage(durable, body, sendWorked);
};
function doSendMessage(durable, body, onSendCompleteFn) {
var selection = workspace.selection;
if (selection) {
var mbean = selection.objectName;
if (mbean) {
var headers = null;
if ($scope.headers.length) {
headers = {};
angular.forEach($scope.headers, function (object) {
var key = object.name;
if (key) {
headers[key] = object.value;
}
});
log.info("About to send headers: " + JSON.stringify(headers));
}
var callback = onSuccess(onSendCompleteFn);
ARTEMIS.log.info("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
var user = localStorage["artemisUserName"];
var pwd = localStorage["artemisPassword"];
// AMQ is sending non persistent by default, so make sure we tell to sent persistent by default
if (!headers) {
headers = {};
}
var type = 3;
ARTEMISService.artemisConsole.sendMessage(mbean, jolokia, headers, type, body, durable, user, pwd, callback, onSuccess(callback));
}
}
}
$scope.fileSelection = function () {
var answer = [];
angular.forEach($scope.selectedFiles, function (value, key) {
if (value) {
answer.push(key);
}
});
return answer;
};
$scope.sendSelectedFiles = function () {
var filesToSend = $scope.fileSelection();
var fileCount = filesToSend.length;
var version = $scope.container.versionId || "1.0";
function onSendFileCompleted(response) {
if (filesToSend.length) {
var fileName = filesToSend.pop();
if (fileName) {
// lets load the file data...
var profile = $scope.profileFileNameToProfileId[fileName];
if (profile) {
var body = Fabric.getConfigFile(jolokia, version, profile, fileName);
if (!body) {
log.warn("No body for message " + fileName);
body = "";
}
doSendMessage(body, onSendFileCompleted);
}
}
}
else {
var text = Core.maybePlural(fileCount, "Message") + " sent!";
Core.notification("success", text);
}
}
// now lets start sending
onSendFileCompleted(null);
};
function isJmsEndpoint() {
// TODO check for the jms/activemq endpoint in ARTEMIS or if its an activemq endpoint
return true;
}
};
return ARTEMIS;
} (ARTEMIS || {}));

View File

@ -0,0 +1,76 @@
/*
* 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.
*/
/// <reference path="artemisPlugin.ts"/>
var ARTEMIS;
(function (ARTEMIS) {
ARTEMIS.module.controller("ARTEMIS.TreeHeaderController", ["$scope", function ($scope) {
$scope.expandAll = function () {
Tree.expandAll("#artemistree");
};
$scope.contractAll = function () {
Tree.contractAll("#artemistree");
};
}]);
ARTEMIS.module.controller("ARTEMIS.TreeController", ["$scope", "$location", "workspace", "localStorage", function ($scope, $location, workspace, localStorage) {
var artemisJmxDomain = localStorage['artemisJmxDomain'] || "org.apache.activemq.artemis";
ARTEMIS.log.info("init tree " + artemisJmxDomain);
$scope.$on("$routeChangeSuccess", function (event, current, previous) {
// lets do this asynchronously to avoid Error: $digest already in progress
setTimeout(updateSelectionFromURL, 50);
});
$scope.$watch('workspace.tree', function () {
reloadTree();
});
$scope.$on('jmxTreeUpdated', function () {
reloadTree();
});
function reloadTree() {
ARTEMIS.log.info("workspace tree has changed, lets reload the artemis tree");
var children = [];
var tree = workspace.tree;
ARTEMIS.log.info("tree="+tree);
if (tree) {
var domainName = artemisJmxDomain;
var folder = tree.get(domainName);
ARTEMIS.log.info("folder="+folder);
if (folder) {
children = folder.children;
}
var treeElement = $("#artemistree");
Jmx.enableTree($scope, $location, workspace, treeElement, children, true);
// lets do this asynchronously to avoid Error: $digest already in progress
setTimeout(updateSelectionFromURL, 50);
}
}
function updateSelectionFromURL() {
Jmx.updateTreeSelectionFromURLAndAutoSelect($location, $("#artemistree"), function (first) {
// use function to auto select the queue folder on the 1st broker
var jms = first.getChildren()[0];
ARTEMIS.log.info("%%%%%%" + jms);
var queues = jms.getChildren()[0];
if (queues && queues.data.title === 'Queue') {
first = queues;
first.expand(true);
return first;
}
return null;
}, true);
}
}]);
})(ARTEMIS || (ARTEMIS = {}));

View File

@ -0,0 +1,82 @@
/*
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.
Architecture
*/
function ArtemisConsole() {
this.getServerAttributes = function (jolokia, mBean) {
var req1 = { type: "read", mbean: mBean};
return jolokia.request(req1, {method: "post"});
};
this.createAddress = function (mbean, jolokia, name, routingType, method) {
jolokia.execute(mbean, "createAddress(java.lang.String,java.lang.String)", name, routingType, method);
};
this.deleteAddress = function (mbean, jolokia, name, method) {
jolokia.execute(mbean, "deleteAddress(java.lang.String)", name, method);
};
this.createQueue = function (mbean, jolokia, address, routingType, name, durable, filter, maxConsumers, purgeWhenNoConsumers, method) {
jolokia.execute(mbean, "createQueue(java.lang.String,java.lang.String,java.lang.String,java.lang.String,boolean,int,boolean,boolean)", address, routingType, name, filter, durable, maxConsumers, purgeWhenNoConsumers, true, method);
};
this.deleteQueue = function (mbean, jolokia, name, method) {
jolokia.execute(mbean, "destroyQueue(java.lang.String)", name, method);
};
this.purgeQueue = function (mbean, jolokia, name, method) {
//todo
};
this.browse = function (mbean, jolokia, method) {
jolokia.request({ type: 'exec', mbean: mbean, operation: 'browse()' }, method);
};
this.deleteMessage = function (mbean, jolokia, id, method) {
ARTEMIS.log.info("executing on " + mbean);
jolokia.execute(mbean, "removeMessage(long)", id, method);
};
this.moveMessage = function (mbean, jolokia, id, queueName, method) {
jolokia.execute(mbean, "moveMessage(java.lang.String,java.lang.String)", id, queueName, method);
};
this.retryMessage = function (mbean, jolokia, id, method) {
jolokia.execute(mbean, "retryMessage(java.lang.String)", id, method);
};
this.sendMessage = function (mbean, jolokia, headers, type, body, durable, user, pwd, method) {
jolokia.execute(mbean, "sendMessage(java.util.Map, int, java.lang.String, boolean, java.lang.String, java.lang.String)", headers, type, body, durable, user, pwd, method);
};
this.getConsumers = function (mbean, jolokia, method) {
jolokia.request({ type: 'exec', mbean: mbean, operation: 'listAllConsumersAsJSON()' }, method);
};
this.getRemoteBrokers = function (mbean, jolokia, method) {
jolokia.request({ type: 'exec', mbean: mbean, operation: 'listNetworkTopology()' }, method);
};
}
function getServerAttributes() {
var console = new ArtemisConsole();
return console.getVersion(new Jolokia("http://localhost:8161/jolokia/"));
}

View File

@ -98,6 +98,7 @@
<modules> <modules>
<module>activemq-branding</module> <module>activemq-branding</module>
<module>artemis-plugin</module>
</modules> </modules>
</project> </project>