/* * * 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.qpid.qmf2.console; // Misc Imports import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; // QMF2 Imports import org.apache.qpid.qmf2.common.ObjectId; import org.apache.qpid.qmf2.common.QmfData; import org.apache.qpid.qmf2.common.QmfException; import org.apache.qpid.qmf2.common.SchemaClass; import org.apache.qpid.qmf2.common.SchemaClassId; /** * Local representation (proxy) of a remote Agent. *

* This class holds some state that relates to the Agent and in addition some methods may be called on the agent. * destroy(), invokeMethod() and refresh() are actually called by a proxy class AgentProxy. AgentProxy is actually * an interface that is implemented by Console (as that's where all the JMS stuff is), we use this approach to * avoid introducing a circular dependency between Agent and Console. *

* The Console application maintains a list of all known remote Agents. * Each Agent is represented by an instance of the Agent class: *

* The following diagram illustrates the interactions between the Console, AgentProxy and the client side Agent * representation. *

* * @author Fraser Adams */ public final class Agent extends QmfData { private AgentProxy _proxy; private List _packages = new ArrayList(); private Map _schemaCache = new ConcurrentHashMap(); private long _epoch; private long _heartbeatInterval; private long _timestamp; private boolean _eventsEnabled = true; private boolean _isActive = true; /** * The main constructor, taking a java.util.Map as a parameter. In essence it "deserialises" its state from the Map. * * @param m the map used to construct the SchemaClass * @param p the AgentProxy instance that implements some of the concrete behaviour of the local Agent representation. */ public Agent(final Map m, final AgentProxy p) { super(m); // Populate attributes translating any old style keys if necessary. _epoch = hasValue("_epoch") ? getLongValue("_epoch") : getLongValue("epoch"); _heartbeatInterval = hasValue("_heartbeat_interval") ? getLongValue("_heartbeat_interval") : getLongValue("heartbeat_interval"); _timestamp = hasValue("_timestamp") ? getLongValue("_timestamp") : getLongValue("timestamp"); _proxy = p; } /** * Sets the state of the Agent, used as an assignment operator. * * @param m the Map used to initialise the Agent. */ @SuppressWarnings("unchecked") public void initialise(final Map m) { Map values = (Map)m.get("_values"); _values = (values == null) ? m : values; // Populate attributes translating any old style keys if necessary. _epoch = hasValue("_epoch") ? getLongValue("_epoch") : getLongValue("epoch"); _heartbeatInterval = hasValue("_heartbeat_interval") ? getLongValue("_heartbeat_interval") : getLongValue("heartbeat_interval"); _timestamp = hasValue("_timestamp") ? getLongValue("_timestamp") : getLongValue("timestamp"); } /** * Return whether or not events are enabled for this Agent. * @return a boolean indication of whether or not events are enabled for this Agent. */ public boolean eventsEnabled() { return _eventsEnabled; } /** * Deactivated this Agent. Called by the Console when the Agent times out. */ public void deactivate() { _isActive = false; } /** * Return the Agent instance name. * @return the Agent instance name. */ public String getInstance() { return getStringValue("_instance"); } /** * Return the identifying name string of the Agent. * @return the identifying name string of the Agent. This name is used to send AMQP messages directly to this agent. */ public String getName() { return getStringValue("_name"); } /** * Return the product name string of the Agent. * @return the product name string of the Agent. */ public String getProduct() { return getStringValue("_product"); } /** * Return the Agent vendor name. * @return the Agent vendor name. */ public String getVendor() { return getStringValue("_vendor"); } /** * Return the Epoch stamp. * @return the Epoch stamp, used to determine if an Agent has been restarted. */ public long getEpoch() { return _epoch; } /** * Set the Epoch stamp. * @param epoch the new Epoch stamp, used to indicate that an Agent has been restarted. */ public void setEpoch(long epoch) { _epoch = epoch; } /** * Return the time that the Agent waits between sending hearbeat messages. * @return the time that the Agent waits between sending hearbeat messages. */ public long getHeartbeatInterval() { return _heartbeatInterval; } /** * Return the timestamp of the Agent's last update. * @return the timestamp of the Agent's last update. */ public long getTimestamp() { return _timestamp; } /** * Return true if the agent is alive. * @return true if the agent is alive (heartbeats have not timed out). */ public boolean isActive() { return _isActive; } /** * Request that the Agent updates the value of this object's contents. * * @param objectId the ObjectId being queried for.. * @param replyHandle the correlation handle used to tie asynchronous method requests with responses. * @param timeout the maximum time to wait for a response, overrides default replyTimeout. * @return the refreshed object. */ public QmfConsoleData refresh(final ObjectId objectId, final String replyHandle, final int timeout) throws QmfException { if (isActive()) { return _proxy.refresh(this, objectId, replyHandle, timeout); } else { throw new QmfException("Agent.refresh() called from deactivated Agent"); } } /** * Helper method to create a Map containing a QMF method request. * * @param objectId the objectId of the remote object. * @param name the remote method name. * @param inArgs the formal parameters of the remote method name. * @return a Map containing a QMF method request. */ private Map createRequest(final ObjectId objectId, final String name, final QmfData inArgs) { // Default sizes for HashMap should be fine for request Map request = new HashMap(); if (objectId != null) { request.put("_object_id", objectId.mapEncode()); } request.put("_method_name", name); if (inArgs != null) { request.put("_arguments", inArgs.mapEncode()); if (inArgs.getSubtypes() != null) { request.put("_subtypes", inArgs.getSubtypes()); } } return request; } /** * Sends a method request to the Agent. Delegates to the AgentProxy to actually send the method as it's the * AgentProxy that knows about connections, sessions and messages. * * @param objectId the objectId of the remote object. * @param name the remote method name. * @param inArgs the formal parameters of the remote method name. * @param timeout the maximum time to wait for a response, overrides default replyTimeout. * @return the MethodResult. */ protected MethodResult invokeMethod(final ObjectId objectId, final String name, final QmfData inArgs, final int timeout) throws QmfException { if (isActive()) { return _proxy.invokeMethod(this, createRequest(objectId, name, inArgs), null, timeout); } else { throw new QmfException("Agent.invokeMethod() called from deactivated Agent"); } } /** * Sends an asynchronous method request to the Agent. Delegates to the AgentProxy to actually send the method as * it's the AgentProxy that knows about connections, sessions and messages. * * @param objectId the objectId of the remote object. * @param name the remote method name. * @param inArgs the formal parameters of the remote method name. * @param replyHandle the correlation handle used to tie asynchronous method requests with responses. */ protected void invokeMethod(final ObjectId objectId, final String name, final QmfData inArgs, final String replyHandle) throws QmfException { if (isActive()) { _proxy.invokeMethod(this, createRequest(objectId, name, inArgs), replyHandle, -1); } else { throw new QmfException("Agent.invokeMethod() called from deactivated Agent"); } } /** * Sends a method request to the Agent. Delegates to the AgentProxy to actually send the method as it's the * AgentProxy that knows about connections, sessions and messages. * * @param name the remote method name. * @param inArgs the formal parameters of the remote method name. * @return the MethodResult. */ public MethodResult invokeMethod(final String name, final QmfData inArgs) throws QmfException { return invokeMethod(null, name, inArgs, -1); } /** * Sends a method request to the Agent. Delegates to the AgentProxy to actually send the method as it's the * AgentProxy that knows about connections, sessions and messages. * * @param name the remote method name. * @param inArgs the formal parameters of the remote method name. * @param timeout the maximum time to wait for a response, overrides default replyTimeout. * @return the MethodResult. */ public MethodResult invokeMethod(final String name, final QmfData inArgs, final int timeout) throws QmfException { return invokeMethod(null, name, inArgs, timeout); } /** * Sends a method request to the Agent. Delegates to the AgentProxy to actually send the method as it's the * AgentProxy that knows about connections, sessions and messages. * * @param name the remote method name. * @param inArgs the formal parameters of the remote method name. * @param replyHandle the correlation handle used to tie asynchronous method requests with responses. */ public void invokeMethod(final String name, final QmfData inArgs, final String replyHandle) throws QmfException { invokeMethod(null, name, inArgs, replyHandle); } /** * Remove a Subscription. Delegates to the AgentProxy to actually remove the Subscription as it's the AgentProxy * that really knows about subscriptions. * * @param subscription the SubscriptionManager that we wish to remove. */ public void removeSubscription(final SubscriptionManager subscription) { _proxy.removeSubscription(subscription); } /** * Allows reception of events from this agent. */ public void enableEvents() { _eventsEnabled = true; } /** * Prevents reception of events from this agent. */ public void disableEvents() { _eventsEnabled = false; } /** * Releases this Agent instance. Once called, the Console application should not reference this instance again. */ public void destroy() { _timestamp = 0; _proxy.destroy(this); } /** * Clears the internally cached schema. Generally done when we wich to refresh the schema information from the * remote Agent. */ public void clearSchemaCache() { _schemaCache.clear(); _packages.clear(); } /** * Stores the schema and package information obtained by querying the remote Agent. * * @param classes the list of SchemaClassIds obtained by querying the remote Agent. */ public void setClasses(final List classes) { if (classes == null) { clearSchemaCache(); return; } for (SchemaClassId classId : classes) { _schemaCache.put(classId, SchemaClass.EMPTY_SCHEMA); if (!_packages.contains(classId.getPackageName())) { _packages.add(classId.getPackageName()); } } } /** * Return the list of SchemaClassIds associated with this Agent. * @return the list of SchemaClassIds associated with this Agent. */ public List getClasses() { if (_schemaCache.size() == 0) { return Collections.emptyList(); } return new ArrayList(_schemaCache.keySet()); } /** * Return the list of packages associated with this Agent. * @return the list of packages associated with this Agent. */ public List getPackages() { return _packages; } /** * Return the SchemaClass associated with this Agent. * @return the list of SchemaClass associated with this Agent. *

* I believe that there should only be one entry in the list returned when looking up a specific chema by classId. */ public List getSchema(final SchemaClassId classId) { SchemaClass schema = _schemaCache.get(classId); if (schema == SchemaClass.EMPTY_SCHEMA) { return Collections.emptyList(); } List results = new ArrayList(); results.add(schema); return results; } /** * Set a schema keyed by SchemaClassId. * * @param classId the SchemaClassId indexing the particular schema. * @param schemaList the schema being indexed. *

* I believe that there should only be one entry in the list returned when looking up a specific chema by classId. */ public void setSchema(final SchemaClassId classId, final List schemaList) { if (schemaList == null || schemaList.size() == 0) { _schemaCache.put(classId, SchemaClass.EMPTY_SCHEMA); } else { // I believe that there should only be one entry in the list returned when looking up // a specific chema by classId _schemaCache.put(classId, schemaList.get(0)); } } /** * Helper/debug method to list the QMF Object properties and their type. */ @Override public void listValues() { super.listValues(); System.out.println("Agent:"); System.out.println("instance: " + getInstance()); System.out.println("name: " + getName()); System.out.println("product: " + getProduct()); System.out.println("vendor: " + getVendor()); System.out.println("epoch: " + getEpoch()); System.out.println("heartbeatInterval: " + getHeartbeatInterval()); System.out.println("timestamp: " + new Date(getTimestamp()/1000000l)); } }