| /* |
| * |
| * 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.management.domain.model; |
| |
| import java.nio.ByteBuffer; |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.management.Attribute; |
| import javax.management.AttributeList; |
| import javax.management.AttributeNotFoundException; |
| import javax.management.InvalidAttributeValueException; |
| import javax.management.MBeanAttributeInfo; |
| import javax.management.MBeanException; |
| import javax.management.MBeanInfo; |
| import javax.management.ReflectionException; |
| import javax.management.RuntimeOperationsException; |
| |
| import org.apache.qpid.management.Messages; |
| import org.apache.qpid.management.domain.model.type.Binary; |
| import org.apache.qpid.transport.codec.BBDecoder; |
| |
| /** |
| * Qpid event definition. |
| * |
| * @author Andrea Gazzarini |
| */ |
| class QpidEvent extends QpidEntity |
| { |
| |
| /** |
| * State interface for this event definition. |
| * Each state is responsible to handle the injection of the data and / or schema. |
| * |
| * @author Andrea Gazzarini |
| */ |
| interface State |
| { |
| /** |
| * Adds the given data for the object instance associated to the given object identifier. |
| * |
| * @param rawData the raw configuration data. |
| */ |
| void addNewEventData (byte[] rawData, long currentTimestamp, int severity); |
| |
| /** |
| * Inject the schema into this class definition. |
| * |
| * @param propertyDefinitions |
| * @param statisticDefinitions |
| * @param methodDefinitions |
| * @throws UnableToBuildFeatureException when it's not possibile to parse schema and build the class definition. |
| */ |
| public void setSchema (List<Map<String, Object>> agumentDefinitions) throws UnableToBuildFeatureException; |
| }; |
| |
| |
| /** |
| * This is the initial state of every qpid class. |
| * The class definition instance is created but its schema has not been injected. |
| * Incoming configuration & instrumentation data will be stored in raw format because we don't know how to |
| * parse it until the schema arrives. |
| * In addition, this state is responsible (when data arrives) to request its schema. |
| */ |
| final State _schemaNotRequested = new State() { |
| |
| /** |
| * Stores the incoming data in raw format and request the schema for this class. |
| * After that a transition to the next state is made. |
| * |
| * @param objectId the object instance identifier. |
| * @param rawData incoming configuration data. |
| */ |
| public synchronized void addNewEventData (byte[] rawData, long currentTimestamp, int severity) |
| { |
| try |
| { |
| requestSchema(); |
| _state = _schemaRequestedButNotYetInjected; |
| } catch (Exception e) |
| { |
| _logger.error( |
| Messages.QMAN_100015_UNABLE_TO_SEND_SCHEMA_REQUEST, |
| _parent.getName(), |
| _name); |
| } finally { |
| createEventInstance(rawData,currentTimestamp,severity); |
| } |
| } |
| |
| /** |
| * This method only throws an illegal state exception because when a schema arrives |
| * this state is no longer valid. |
| */ |
| public void setSchema (List<Map<String, Object>> agumentDefinitions) throws UnableToBuildFeatureException |
| { |
| throw new IllegalStateException("When a schema arrives it's not possible for this class to be in this state."); |
| } |
| }; |
| |
| /** |
| * This is the first state of this class definition : the schema is not yet injected so each injection of object data will be |
| * retained in raw format. |
| */ |
| final State _schemaRequestedButNotYetInjected = new State() |
| { |
| /** |
| * Stores the incoming data in raw format and request the schema for this class. |
| * After that a transition to the next state is made. |
| * |
| * @param objectId the object instance identifier. |
| * @param rawData incoming configuration data. |
| */ |
| public synchronized void addNewEventData (byte[] rawData,long currentTimestamp, int severity) |
| { |
| createEventInstance(rawData,currentTimestamp, severity); |
| } |
| |
| /** |
| * When a schema is injected into this defintiion the following should happen : |
| * 1) the incoming schema is parsed and the class definition is built; |
| * 2) the retained raw data is converted into object instance(s) |
| * 3) the internal state of this class changes; |
| * |
| * If someting is wrong during that process the schema is not built and the state don't change. |
| */ |
| public synchronized void setSchema (List<Map<String, Object>> argumentDefinitions) throws UnableToBuildFeatureException |
| { |
| MBeanAttributeInfo [] attributesMetadata = new MBeanAttributeInfo[argumentDefinitions.size()+2]; |
| |
| buildArguments(argumentDefinitions, attributesMetadata); |
| |
| _metadata = new MBeanInfo(_name,_name,attributesMetadata,null,null,null); |
| |
| // Converting stored object instances into JMX MBean and removing raw instance data. |
| for (QpidManagedEvent instance : _eventInstances) |
| { |
| updateEventInstanceWithData(instance); |
| JMX_SERVICE.registerEventInstance(instance,_parent.getOwnerId(),_parent.getName(),_name); |
| } |
| _state = _schemaInjected; |
| } |
| }; |
| |
| /** |
| * After a schema is built into this definition this is the current state of the class. |
| */ |
| final State _schemaInjected = new State() |
| { |
| /** |
| * Updates the configuration state of the object instance associates with the given object identifier. |
| * |
| * @param objectId the object identifier. |
| * @param rawData the configuration data (raw format). |
| */ |
| public void addNewEventData (byte[] rawData,long currentTimestamp, int severity) |
| { |
| QpidManagedEvent instance = createEventInstance(rawData,currentTimestamp, severity); |
| updateEventInstanceWithData(instance); |
| JMX_SERVICE.registerEventInstance(instance,_parent.getOwnerId(),_parent.getName(),_name); |
| } |
| |
| /** |
| * Never called when the class definition has this state. |
| */ |
| public void setSchema (List<Map<String, Object>> agumentDefinitions) throws UnableToBuildFeatureException |
| { |
| // N.A. : Schema is already injected. |
| } |
| }; |
| |
| /** |
| * MBean used for representing remote broker object instances. |
| * This is the core component of the QMan domain model |
| * |
| * @author Andrea Gazzarini |
| */ |
| class QpidManagedEvent extends QpidManagedEntity |
| { |
| |
| |
| // Arrays used for storing raw data before this mbean is registered to mbean server. |
| final byte[] _rawEventData; |
| final long _timestamp; |
| final int _severity; |
| |
| /** |
| * Builds a new managed object with the given object identifier. |
| * |
| * @param objectId the object identifier. |
| */ |
| private QpidManagedEvent(byte [] data, long timestamp, int severity) |
| { |
| this._rawEventData = data; |
| this._timestamp = timestamp; |
| this._severity = severity; |
| _attributes.put(SEVERITY_ATTR_NAME, _severity); |
| _attributes.put(TIMESTAMP_ATTR_NAME, new Date(_timestamp)); |
| } |
| |
| /** |
| * Returns the value of the given attribute.s |
| * |
| * @throws AttributeNotFoundException when no attribute is found with the given name. |
| */ |
| public Object getAttribute (String attributeName) throws AttributeNotFoundException, MBeanException, ReflectionException |
| { |
| if (attributeName == null) |
| { |
| throw new RuntimeOperationsException(new IllegalArgumentException("Attribute name must not be null.")); |
| } |
| |
| if (_arguments.containsKey(attributeName) || SEVERITY_ATTR_NAME.equals(attributeName) || TIMESTAMP_ATTR_NAME.equals(attributeName)) |
| { |
| return _attributes.get(attributeName); |
| } else |
| { |
| throw new AttributeNotFoundException(attributeName); |
| } |
| } |
| |
| /** |
| * Executes an operation on this object instance. |
| * |
| * @param actionName the name of the method. |
| * @param params the method parameters |
| * @param signature the method signature. |
| */ |
| public Object invoke (String actionName, Object[] params, String[] signature) throws MBeanException,ReflectionException |
| { |
| throw new ReflectionException(new NoSuchMethodException(actionName)); |
| } |
| |
| /** |
| * Sets the value of the given attribute on this object instance. |
| * |
| * @param attribute contains the new value of the attribute. |
| * @throws AttributeNotFoundException when the given attribute is not found on this object instance. |
| * @throws InvalidAttributeValueException when the given value is violating one attribute invariant. |
| */ |
| public void setAttribute (Attribute attribute) throws AttributeNotFoundException, |
| InvalidAttributeValueException, MBeanException, ReflectionException |
| { |
| throw new ReflectionException(new NoSuchMethodException()); |
| } |
| |
| /** |
| * Sets the values of several attributes of this MBean. |
| * |
| * @param attributes a list of attributes: The identification of the attributes to be set and the values they are to be set to. |
| * @return The list of attributes that were set, with their new values. |
| */ |
| public AttributeList setAttributes (AttributeList attributes) |
| { |
| throw new RuntimeException(); |
| } |
| } |
| |
| final static String SEVERITY_ATTR_NAME = "Severity"; |
| final static String TIMESTAMP_ATTR_NAME = "Date"; |
| |
| private List<QpidProperty> _schemaOrderedArguments = new ArrayList<QpidProperty>(); |
| |
| Map<String, QpidProperty> _arguments = new HashMap<String, QpidProperty>(); |
| List<QpidManagedEvent> _eventInstances = new LinkedList<QpidManagedEvent>(); |
| State _state = _schemaNotRequested;; |
| |
| /** |
| * Builds a new class with the given name and package as parent. |
| * |
| * @param className the name of the class. |
| * @param hash the class schema hash. |
| * @param parentPackage the parent of this class. |
| */ |
| QpidEvent(String eventClassName, Binary hash, QpidPackage parentPackage) |
| { |
| super(eventClassName,hash,parentPackage); |
| } |
| |
| /** |
| * Adds the configuration data for the object instance associated to the given object identifier. |
| * |
| * @param objectId the object identifier. |
| * @param rawData the raw configuration data. |
| */ |
| void addEventData (byte[] rawData, long currentTimestamp, int severity) |
| { |
| _logger.debug( |
| Messages.QMAN_200021_INCOMING_EVENT_DATA, |
| _parent.getOwnerId(), |
| _parent.getName(), |
| _name); |
| _state.addNewEventData(rawData, currentTimestamp, severity); |
| } |
| |
| /** |
| * Sets the schema for this class definition. |
| * A schema is basically a metadata description of all properties, statistics, methods and events of this class. |
| * |
| * @param propertyDefinitions properties metadata. |
| * @param statisticDefinitions statistics metadata. |
| * @param methodDefinitions methods metadata. |
| * @throws UnableToBuildFeatureException when some error occurs while parsing the incoming schema. |
| */ |
| void setSchema (List<Map<String, Object>> argumentDefinitions) throws UnableToBuildFeatureException |
| { |
| _logger.info(Messages.QMAN_000010_INCOMING_SCHEMA,_parent.getOwnerId(),_parent.getName(),_name); |
| _state.setSchema(argumentDefinitions); |
| } |
| |
| /** |
| * Internal method used for building attributes definitions. |
| * |
| * @param props the map contained in the properties schema. |
| * @param stats the map contained in the statistics schema. |
| * @param attributes the management metadata for attributes. |
| * @throws UnableToBuildFeatureException when it's not possibile to build one attribute definition. |
| */ |
| void buildArguments ( |
| List<Map<String, Object>> arguments,MBeanAttributeInfo[] attributes) throws UnableToBuildFeatureException |
| { |
| int index = 0; |
| |
| for (Map<String, Object> argumentDefinition : arguments) |
| { |
| // Force metadata attributes. It is needed because arguments are "similar" to properties but they |
| // aren't properties and then they haven't optional, index and access metadata attributes |
| // (mandatory for build a property definition). |
| argumentDefinition.put(QpidFeatureBuilder.Attribute.optional.name(),0); |
| argumentDefinition.put(QpidFeatureBuilder.Attribute.index.name(),1); |
| argumentDefinition.put(QpidFeatureBuilder.Attribute.access.name(),3); |
| |
| QpidFeatureBuilder builder = QpidFeatureBuilder.createPropertyBuilder(argumentDefinition); |
| builder.build(); |
| |
| QpidProperty argument = (QpidProperty) builder.getQpidFeature(); |
| |
| _arguments.put(argument.getName(),argument); |
| _schemaOrderedArguments.add(argument); |
| attributes[index++]=(MBeanAttributeInfo) builder.getManagementFeature(); |
| |
| _logger.debug( |
| Messages.QMAN_200019_EVENT_ARGUMENT_DEFINITION_HAS_BEEN_BUILT, |
| _parent.getName(), |
| _name, |
| argument); |
| } |
| |
| attributes[index++] = new MBeanAttributeInfo( |
| SEVERITY_ATTR_NAME, |
| Integer.class.getName(), |
| Messages.EVENT_SEVERITY_ATTRIBUTE_DESCRIPTION, |
| true, |
| false, |
| false); |
| |
| attributes[index++] = new MBeanAttributeInfo( |
| TIMESTAMP_ATTR_NAME, |
| Date.class.getName(), |
| Messages.EVENT_TIMESTAMP_ATTRIBUTE_DESCRIPTION, |
| true, |
| false, |
| false); |
| } |
| |
| /** |
| * Returns the object instance associated to the given identifier. |
| * Note that if the identifier is not associated to any obejct instance, a new one will be created. |
| * |
| * @param objectId the object identifier. |
| * @param registration a flag indicating whenever the (new ) instance must be registered with MBean server. |
| * @return the object instance associated to the given identifier. |
| */ |
| QpidManagedEvent createEventInstance(byte [] data, long timestamp, int severity) |
| { |
| QpidManagedEvent eventInstance = new QpidManagedEvent(data, timestamp, severity); |
| _eventInstances.add(eventInstance); |
| return eventInstance; |
| } |
| |
| /** |
| * Updates the given obejct instance with the given incoming configuration data. |
| * |
| * @param instance the managed object instance. |
| * @param rawData the incoming configuration data which contains new values for instance properties. |
| */ |
| void updateEventInstanceWithData(QpidManagedEvent instance) |
| { |
| BBDecoder decoder = new BBDecoder(); |
| decoder.init(ByteBuffer.wrap(instance._rawEventData)); |
| |
| for (QpidProperty property : _schemaOrderedArguments) |
| { |
| try { |
| Object value = property.decodeValue(decoder); |
| instance.createOrReplaceAttributeValue(property.getName(),value); |
| } catch(Exception ignore) { |
| _logger.error(Messages.QMAN_100016_UNABLE_TO_DECODE_FEATURE_VALUE, _parent.getName(),_name,property.getName()); |
| } |
| } |
| } |
| |
| @Override |
| public String toString () |
| { |
| return new StringBuilder() |
| .append(_parent.getOwnerId()) |
| .append("::") |
| .append(_parent.getName()) |
| .append(".") |
| .append(_name) |
| .toString(); |
| } |
| |
| /** |
| * Deregisters all the object instances and release all previously acquired resources. |
| */ |
| void releaseResources () |
| { |
| _eventInstances.clear(); |
| JMX_SERVICE.unregisterEvents(); |
| _service.close(); |
| } |
| |
| /** |
| * Checks if this event definition contains event instance(s). |
| * |
| * @return true if there is one or more managed instances. |
| */ |
| boolean hasNoInstances() |
| { |
| return _eventInstances.isEmpty(); |
| } |
| } |