use of org.apache.qpid.qmf2.console.Console in project qpid by apache.
the class QpidServer method doGet.
/**
* Called by the Web Server to allow a Server to handle a GET request.
* The HTTP GET URL structure for the REST API is specified above in the overall class documentation.
*
* @param tx the HttpTransaction containing the request from the client and used to send the response.
*/
public void doGet(final HttpTransaction tx) throws IOException {
String path = tx.getRequestURI();
if (path.startsWith("/qpid/connection/")) {
path = path.substring(17);
// Using the principal lets different users use the default connection.
String user = tx.getPrincipal();
if (path.length() == 0) {
// handle "/qpid/connection/" request with unspecified connection (returns list of available connections).
tx.sendResponse(HTTP_OK, "application/json", JSON.fromObject(_connections.getAll(user)));
} else {
// if path.length() > 0 we're dealing with a specified Connection so extract the name and look it up.
String connectionName = path;
int i = path.indexOf("/");
if (// Can use > rather than >= as we've already tested for "/qpid/connection/" above.
i > 0) {
connectionName = path.substring(0, i);
path = path.substring(i + 1);
} else {
path = "";
}
connectionName = user + "." + connectionName;
// attempts to connect to a broker specified in the QpidRestAPI config (or default 0.0.0.0:5672).
if (connectionName.equals(user + ".default")) {
ConnectionProxy defaultConnection = _connections.get(connectionName);
if (defaultConnection == null) {
defaultConnection = _connections.create(connectionName, _defaultBroker, "", false);
// Wait a maximum of 1000ms for the underlying Qpid Connection to become available. If we
// don't do this the first call using the default will return 404 Not Found.
defaultConnection.waitForConnection(1000);
}
}
// Find the Connection with the name extracted from the URI.
ConnectionProxy connection = _connections.get(connectionName);
if (connection == null) {
tx.sendResponse(HTTP_NOT_FOUND, "text/plain", "404 Not Found.");
} else if (!connection.isConnected()) {
tx.sendResponse(HTTP_INTERNAL_ERROR, "text/plain", "500 Broker Disconnected.");
} else {
if (path.length() == 0) {
// handle request for information about a specified Console
tx.sendResponse(HTTP_OK, "application/json", connection.toString());
} else {
// In this block we are dealing with resources associated with a specified connectionName.
// path describes the resources specifically related to "/qpid/connection/<connectionName>"
Console console = connection.getConsole();
if (path.startsWith("console/objects/")) {
// Get information about specified objects.
path = path.substring(16);
sendGetObjectsResponse(tx, console, path);
} else if (path.startsWith("console/objects") && path.length() == 15) {
// If objects is unspecified treat as a synonym for classes.
tx.sendResponse(HTTP_OK, "application/json", JSON.fromObject(console.getClasses()));
} else if (path.startsWith("console/address/")) {
// Get the Console AMQP Address
tx.sendResponse(HTTP_OK, "application/json", JSON.fromObject(console.getAddress()));
} else if (path.startsWith("console/address") && path.length() == 15) {
// Get the Console AMQP Address
tx.sendResponse(HTTP_OK, "application/json", JSON.fromObject(console.getAddress()));
} else if (path.startsWith("console/workItemCount/")) {
// Returns the count of pending WorkItems that can be retrieved.
tx.sendResponse(HTTP_OK, "text/plain", "" + console.getWorkitemCount());
} else if (path.startsWith("console/workItemCount") && path.length() == 21) {
// Returns the count of pending WorkItems that can be retrieved.
tx.sendResponse(HTTP_OK, "text/plain", "" + console.getWorkitemCount());
} else if (path.startsWith("console/nextWorkItem/")) {
// Obtains the next pending work item, or null if none available.
tx.sendResponse(HTTP_OK, "application/json", JSON.fromObject(console.getNextWorkitem()));
} else if (path.startsWith("console/nextWorkItem") && path.length() == 20) {
// Obtains the next pending work item, or null if none available.
tx.sendResponse(HTTP_OK, "application/json", JSON.fromObject(console.getNextWorkitem()));
} else if (path.startsWith("console/agents") && path.length() == 14) {
// Get information about all available Agents.
tx.sendResponse(HTTP_OK, "application/json", JSON.fromObject(console.getAgents()));
} else if (path.startsWith("console/agent/")) {
// Get information about a specified Agent.
Agent agent = console.getAgent(path.substring(14));
if (agent == null) {
tx.sendResponse(HTTP_NOT_FOUND, "text/plain", "404 Not Found.");
} else {
tx.sendResponse(HTTP_OK, "application/json", JSON.fromObject(agent));
}
} else if (path.startsWith("console/agent") && path.length() == 13) {
// If agent is unspecified treat as a synonym for agents.
tx.sendResponse(HTTP_OK, "application/json", JSON.fromObject(console.getAgents()));
} else if (path.startsWith("console/classes/")) {
// Get information about the classes for a specified Agent
// Get Agent name
path = path.substring(16);
// TODO handle getClasses() for specified Agent
tx.sendResponse(HTTP_NOT_IMPLEMENTED, "text/plain", "501 getClasses() for specified Agent not yet implemented.");
} else if (path.startsWith("console/classes") && path.length() == 15) {
// Get information about all the classes for all Agents
tx.sendResponse(HTTP_OK, "application/json", JSON.fromObject(console.getClasses()));
} else if (path.startsWith("console/packages/")) {
// Get information about the packages for a specified Agent
// Get Agent name
path = path.substring(17);
// TODO handle getPackages() for specified Agent.
tx.sendResponse(HTTP_NOT_IMPLEMENTED, "text/plain", "501 getPackages() for specified Agent not yet implemented.");
} else if (path.startsWith("object/")) {
/**
* This is the REST implementation of getObjects(oid) it is also the equivalent of
* the QmfConsoleData refresh() method where an object can update its state.
* N.B. that the ManagementAgent on the broker appears not to set the timestamp properties
* in the response to this call, which means that they get set to current time in the
* QmfConsoleData, this is OK for _update_ts but not for _create_ts and _delete_ts
* users of this call should be aware of that in their own code.
*/
path = path.substring(7);
// The ObjectId has been passed in the URI, create a real ObjectId
ObjectId oid = new ObjectId(path);
List<QmfConsoleData> objects = console.getObjects(oid);
if (objects.size() == 0) {
tx.sendResponse(HTTP_NOT_FOUND, "text/plain", "404 Not Found.");
} else {
// Not that in a departure from the QMF2 API this returns the QmfConsoleData object
// rather than a list of size one. Perhaps the APIs should be completely consistent
// but this response seems more convenient.
tx.sendResponse(HTTP_OK, "application/json", JSON.fromObject(objects.get(0)));
}
} else if (path.startsWith("console/packages") && path.length() == 16) {
// Get information about all the packages for all Agents
tx.sendResponse(HTTP_OK, "application/json", JSON.fromObject(console.getPackages()));
} else {
tx.sendResponse(HTTP_NOT_FOUND, "text/plain", "404 Not Found.");
}
}
}
}
} else if (path.startsWith("/qpid/connection")) {
// handle "/qpid/connection" request with unspecified connection (returns list of available connections).
// Using the principal lets different users use the default connection.
String user = tx.getPrincipal();
tx.sendResponse(HTTP_OK, "application/json", JSON.fromObject(_connections.getAll(user)));
} else {
tx.sendResponse(HTTP_NOT_FOUND, "text/plain", "404 Not Found.");
}
}
use of org.apache.qpid.qmf2.console.Console in project qpid by apache.
the class QpidServer method doPost.
/**
* Called by the Web Server to allow a Server to handle a POST request.
* <pre>
* POST: <host>:<port>/qpid/connection/<name>/object/<ObjectId>
* HTTP body: {"_method_name":<method>,"_arguments":<inArgs>}
* <method>: A string containing the QMF2 method name e.g. "getLogLevel", "setLogLevel", "create", "delete".
* <inArgs>: A JSON string containing the method arguments e.g. {"level":"debug+:Broker"} for setLogLevel.
* HTTP response: A JSON string containing the response e.g. {"level":"notice+"} for getLogLevel (may be empty).
*
* This method invokes the QMF2 method <method> with arguments <inArgs> on the object <ObjectId>
* </pre>
* @param tx the HttpTransaction containing the request from the client and used to send the response.
*/
@SuppressWarnings("unchecked")
public void doPost(final HttpTransaction tx) throws IOException {
String path = tx.getRequestURI();
if (path.startsWith("/qpid/connection/")) {
path = path.substring(17);
String user = tx.getPrincipal();
String connectionName = path;
int i = path.indexOf("/");
if (// Can use > rather than >= as we've already tested for "/qpid/connection/" above.
i > 0) {
connectionName = user + "." + path.substring(0, i);
path = path.substring(i + 1);
// Find the Connection with the name extracted from the URI.
ConnectionProxy connection = _connections.get(connectionName);
if (connection == null) {
_log.info("QpidServer.doPost path: {} Connection not found.", tx.getRequestURI());
tx.sendResponse(HTTP_NOT_FOUND, "text/plain", "404 Not Found.");
} else if (!connection.isConnected()) {
tx.sendResponse(HTTP_INTERNAL_ERROR, "text/plain", "500 Broker Disconnected.");
} else {
// If we get this far we should have found a Qpid Connection so retrieve the QMF2 Console Object.
Console console = connection.getConsole();
if (path.startsWith("object/")) {
path = path.substring(7);
// The ObjectId has been passed in the URI create an ObjectId and retrieve the Agent Name.
ObjectId oid = new ObjectId(path);
String agentName = oid.getAgentName();
// The qpidd ManagementAgent doesn't populate AgentName, if it's empty assume it's the broker.
agentName = agentName.equals("") ? "broker" : agentName;
// Find the Agent we got the QmfData from.
Agent agent = console.getAgent(agentName);
if (agent == null) {
_log.info("QpidServer.doPost path: {} Agent: {} not found.", tx.getRequestURI(), agentName);
tx.sendResponse(HTTP_NOT_FOUND, "text/plain", "404 Not Found.");
} else {
// If we get this far we can create the Object and invoke the method.
// We can create a QmfConsoleData with nothing but an ObjectId and the agent.
QmfConsoleData object = new QmfConsoleData(Collections.EMPTY_MAP, agent);
object.setObjectId(oid);
String request = tx.getRequestString();
_log.info("QpidServer.doPost path: {} body: {}", tx.getRequestURI(), request);
//System.out.println(request);
String method = "";
try {
Map<String, Object> reqMap = JSON.toMap(request);
method = (String) reqMap.get("_method_name");
Object arguments = reqMap.get("_arguments");
Map args = (arguments instanceof Map) ? (Map) arguments : null;
//System.out.println("method: " + method + ", args: " + args);
// Parse the args if present into a QmfData (needed by invokeMethod).
QmfData inArgs = (args == null) ? new QmfData() : new QmfData(args);
// Invoke the specified method on the QmfConsoleData we've created.
MethodResult results = null;
_log.info("invokeMethod: {}", request);
results = object.invokeMethod(method, inArgs);
tx.sendResponse(HTTP_OK, "application/json", JSON.fromObject(results));
} catch (QmfException qmfe) {
_log.info("QpidServer.doPost() caught Exception {}", qmfe.getMessage());
tx.sendResponse(HTTP_INTERNAL_ERROR, "text/plain", "invokeMethod(" + method + ") -> " + qmfe.getMessage());
} catch (Exception e) {
_log.info("QpidServer.doPost() caught Exception {}", e.getMessage());
tx.sendResponse(HTTP_INTERNAL_ERROR, "text/plain", "500 " + e.getMessage());
}
}
} else {
tx.sendResponse(HTTP_NOT_FOUND, "text/plain", "404 Not Found.");
}
}
} else {
tx.sendResponse(HTTP_NOT_FOUND, "text/plain", "404 Not Found.");
}
} else {
tx.sendResponse(HTTP_NOT_FOUND, "text/plain", "404 Not Found.");
}
}
use of org.apache.qpid.qmf2.console.Console in project qpid by apache.
the class ConnectionAudit method onEvent.
/**
* Handles WorkItems delivered by the Console.
* <p>
* If we receive an EventReceivedWorkItem check if it is a subscribe event. If it is we check if the whitelist has
* changed, and if it has we re-read it. We then extract the queue name, exchange name, binding, connection address
* and timestamp and validate with the whitelsist.
* <p>
* If we receive an AgentRestartedWorkItem we revalidate all subscriptions as it's possible that a client connection
* could have been made to the broker before ConnectionAudit has successfully re-established its own connections.
* @param wi a QMF2 WorkItem object
*/
public void onEvent(final WorkItem wi) {
if (wi instanceof EventReceivedWorkItem) {
EventReceivedWorkItem item = (EventReceivedWorkItem) wi;
QmfEvent event = item.getEvent();
String className = event.getSchemaClassId().getClassName();
if (className.equals("subscribe")) {
readWhitelist();
String queueName = event.getStringValue("qName");
String address = event.getStringValue("rhost");
String timestamp = new Date(event.getTimestamp() / 1000000l).toString();
validateQueue(queueName, address, timestamp);
}
} else if (wi instanceof AgentRestartedWorkItem) {
checkExistingSubscriptions();
}
}
use of org.apache.qpid.qmf2.console.Console in project qpid by apache.
the class Console method onMessage.
/**
* MessageListener for QMF2 Agent Events, Hearbeats and Asynchronous data indications
*
* @param message the JMS Message passed to the listener
*/
public void onMessage(Message message) {
try {
String agentName = QmfData.getString(message.getObjectProperty("qmf.agent"));
String content = QmfData.getString(message.getObjectProperty("qmf.content"));
String opcode = QmfData.getString(message.getObjectProperty("qmf.opcode"));
if (opcode.equals("_agent_heartbeat_indication") || opcode.equals("_agent_locate_response")) {
// This block handles Agent lifecycle information (discover, register, delete)
if (_agents.containsKey(agentName)) {
// This block handles Agents that have previously been registered
Agent agent = _agents.get(agentName);
long originalEpoch = agent.getEpoch();
// If we already know about an Agent we simply update the Agent's state using initialise()
agent.initialise(AMQPMessage.getMap(message));
// If the Epoch has changed it means the Agent has been restarted so we send a notification
if (agent.getEpoch() != originalEpoch) {
// Clear cache to force a lookup
agent.clearSchemaCache();
List<SchemaClassId> classes = getClasses(agent);
// Discover the schema for this Agent and cache it
getSchema(classes, agent);
_log.info("Agent {} has been restarted", agentName);
if (_discoverAgents && (_agentQuery == null || _agentQuery.evaluate(agent))) {
_eventListener.onEvent(new AgentRestartedWorkItem(agent));
}
} else {
// Otherwise just send a heartbeat notification
_log.info("Agent {} heartbeat", agent.getName());
if (_discoverAgents && (_agentQuery == null || _agentQuery.evaluate(agent))) {
_eventListener.onEvent(new AgentHeartbeatWorkItem(agent));
}
}
} else {
// This block handles Agents that haven't already been registered
Agent agent = new Agent(AMQPMessage.getMap(message), this);
List<SchemaClassId> classes = getClasses(agent);
// Discover the schema for this Agent and cache it
getSchema(classes, agent);
_agents.put(agentName, agent);
_log.info("Adding Agent {}", agentName);
// the Agent more "user friendly" than using the full Agent name.
if (agent.getVendor().equals("apache.org") && agent.getProduct().equals("qpidd")) {
_log.info("Recording {} as _brokerAgentName", agentName);
_brokerAgentName = agentName;
}
// wait for the broker Agent to become available.
if (_brokerAgentName != null) {
synchronized (this) {
_agentAvailable = true;
notifyAll();
}
}
if (_discoverAgents && (_agentQuery == null || _agentQuery.evaluate(agent))) {
_eventListener.onEvent(new AgentAddedWorkItem(agent));
}
}
// The broker Agent sends periodic heartbeats and that Agent should *always* be available given
// a running broker, so we should get here every "--mgmt-pub-interval" seconds or so, so it's
// a good place to periodically check for the expiry of any other Agents.
handleAgentExpiry();
return;
}
if (!_agents.containsKey(agentName)) {
_log.info("Ignoring Event from unregistered Agent {}", agentName);
return;
}
Agent agent = _agents.get(agentName);
if (!agent.eventsEnabled()) {
_log.info("{} has disabled Event reception, ignoring Event", agentName);
return;
}
// If we get to here the Agent from whence the Event came should be registered and should
// have Event reception enabled, so we should be able to send events to the EventListener
Handle handle = new Handle(message.getJMSCorrelationID());
if (opcode.equals("_method_response") || opcode.equals("_exception")) {
if (AMQPMessage.isAMQPMap(message)) {
_eventListener.onEvent(new MethodResponseWorkItem(handle, new MethodResult(AMQPMessage.getMap(message))));
} else {
_log.info("onMessage() Received Method Response message in incorrect format");
}
}
// refresh() call on QmfConsoleData so the number of results in the returned list *should* be one.
if (opcode.equals("_query_response") && content.equals("_data")) {
if (AMQPMessage.isAMQPList(message)) {
List<Map> list = AMQPMessage.getList(message);
for (Map m : list) {
_eventListener.onEvent(new ObjectUpdateWorkItem(handle, new QmfConsoleData(m, agent)));
}
} else {
_log.info("onMessage() Received Query Response message in incorrect format");
}
}
// This block handles responses to createSubscription and refreshSubscription
if (opcode.equals("_subscribe_response")) {
if (AMQPMessage.isAMQPMap(message)) {
String correlationId = message.getJMSCorrelationID();
SubscribeParams params = new SubscribeParams(correlationId, AMQPMessage.getMap(message));
String subscriptionId = params.getSubscriptionId();
if (subscriptionId != null && correlationId != null) {
SubscriptionManager subscription = _subscriptionById.get(subscriptionId);
if (subscription == null) {
// This is a createSubscription response so the correlationId should be the consoleHandle
subscription = _subscriptionByHandle.get(correlationId);
if (subscription != null) {
_subscriptionById.put(subscriptionId, subscription);
subscription.setSubscriptionId(subscriptionId);
subscription.setDuration(params.getLifetime());
String replyHandle = subscription.getReplyHandle();
if (replyHandle == null) {
subscription.signal();
} else {
_eventListener.onEvent(new SubscribeResponseWorkItem(new Handle(replyHandle), params));
}
}
} else {
// This is a refreshSubscription response
params.setConsoleHandle(subscription.getConsoleHandle());
subscription.setDuration(params.getLifetime());
subscription.refresh();
_eventListener.onEvent(new SubscribeResponseWorkItem(handle, params));
}
}
} else {
_log.info("onMessage() Received Subscribe Response message in incorrect format");
}
}
// Subscription Indication - in other words the asynchronous results of a Subscription
if (opcode.equals("_data_indication") && content.equals("_data")) {
if (AMQPMessage.isAMQPList(message)) {
String consoleHandle = handle.getCorrelationId();
if (consoleHandle != null && _subscriptionByHandle.containsKey(consoleHandle)) {
// If we have a valid consoleHandle the data has come from a "real" Subscription.
List<Map> list = AMQPMessage.getList(message);
List<QmfConsoleData> resultList = new ArrayList<QmfConsoleData>(list.size());
for (Map m : list) {
resultList.add(new QmfConsoleData(m, agent));
}
_eventListener.onEvent(new SubscriptionIndicationWorkItem(new SubscribeIndication(consoleHandle, resultList)));
} else if (_subscriptionEmulationEnabled && agentName.equals(_brokerAgentName)) {
// If the data has come from is the broker Agent we emulate a Subscription on the Console
for (SubscriptionManager subscription : _subscriptionByHandle.values()) {
QmfQuery query = subscription.getQuery();
if (subscription.getAgent().getName().equals(_brokerAgentName) && query.getTarget() == QmfQueryTarget.OBJECT) {
// Only evaluate broker Agent subscriptions with QueryTarget == OBJECT on the Console.
long objectEpoch = 0;
consoleHandle = subscription.getConsoleHandle();
List<Map> list = AMQPMessage.getList(message);
List<QmfConsoleData> resultList = new ArrayList<QmfConsoleData>(list.size());
for (Map m : list) {
// Evaluate the QmfConsoleData object against the query
QmfConsoleData object = new QmfConsoleData(m, agent);
if (query.evaluate(object)) {
long epoch = object.getObjectId().getAgentEpoch();
objectEpoch = (epoch > objectEpoch && !object.isDeleted()) ? epoch : objectEpoch;
resultList.add(object);
}
}
if (resultList.size() > 0) {
// data from the restarted Agent (in case they need to reset any state).
if (objectEpoch > agent.getEpoch()) {
agent.setEpoch(objectEpoch);
// Clear cache to force a lookup
agent.clearSchemaCache();
List<SchemaClassId> classes = getClasses(agent);
// Discover the schema for this Agent and cache it
getSchema(classes, agent);
_log.info("Agent {} has been restarted", agentName);
if (_discoverAgents && (_agentQuery == null || _agentQuery.evaluate(agent))) {
_eventListener.onEvent(new AgentRestartedWorkItem(agent));
}
}
_eventListener.onEvent(new SubscriptionIndicationWorkItem(new SubscribeIndication(consoleHandle, resultList)));
}
}
}
}
} else {
_log.info("onMessage() Received Subscribe Indication message in incorrect format");
}
}
// The results of an Event delivered from an Agent
if (opcode.equals("_data_indication") && content.equals("_event")) {
// There are differences in the type of message sent by Qpid 0.8 and 0.10 onwards.
if (AMQPMessage.isAMQPMap(message)) {
// 0.8 broker passes Events as amqp/map encoded as MapMessages (we convert into java.util.Map)
_eventListener.onEvent(new EventReceivedWorkItem(agent, new QmfEvent(AMQPMessage.getMap(message))));
} else if (AMQPMessage.isAMQPList(message)) {
// 0.10 and above broker passes Events as amqp/list encoded as BytesMessage (needs decoding)
// 0.20 encodes amqp/list in a MapMessage!!?? AMQPMessage hopefully abstracts this detail.
List<Map> list = AMQPMessage.getList(message);
for (Map m : list) {
_eventListener.onEvent(new EventReceivedWorkItem(agent, new QmfEvent(m)));
}
} else {
_log.info("onMessage() Received Event message in incorrect format");
}
}
} catch (JMSException jmse) {
_log.info("JMSException {} caught in onMessage()", jmse.getMessage());
}
}
use of org.apache.qpid.qmf2.console.Console in project qpid by apache.
the class Agent method sendSubscriptionIndicate.
// methods implementing SubscribableAgent interface
// ********************************************************************************************************
/**
* Send a list of updated subscribed data to the Console.
*
* @param handle the console reply handle.
* @param results a list of subscribed data in Map encoded form.
*/
public final void sendSubscriptionIndicate(final Handle handle, final List<Map> results) {
try {
Message response = AMQPMessage.createListMessage(_syncSession);
response.setJMSCorrelationID(handle.getCorrelationId());
response.setStringProperty("x-amqp-0-10.app-id", "qmf2");
response.setStringProperty("method", "indication");
response.setStringProperty("qmf.opcode", "_data_indication");
response.setStringProperty("qmf.content", "_data");
response.setStringProperty("qmf.agent", _name);
response.setStringProperty("qpid.subject", handle.getRoutingKey());
AMQPMessage.setList(response, results);
sendResponse(handle, response);
} catch (JMSException jmse) {
_log.info("JMSException {} caught in sendSubscriptionIndicate()", jmse.getMessage());
}
}
Aggregations