001 /*
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements. See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership. The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License. You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied. See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 *
020 */
021 package org.apache.qpid.management.domain.model;
022
023 import java.nio.ByteBuffer;
024 import java.util.ArrayList;
025 import java.util.Date;
026 import java.util.HashMap;
027 import java.util.LinkedList;
028 import java.util.List;
029 import java.util.Map;
030 import java.util.UUID;
031
032 import javax.management.Attribute;
033 import javax.management.AttributeList;
034 import javax.management.AttributeNotFoundException;
035 import javax.management.InvalidAttributeValueException;
036 import javax.management.MBeanAttributeInfo;
037 import javax.management.MBeanException;
038 import javax.management.MBeanInfo;
039 import javax.management.ObjectName;
040 import javax.management.ReflectionException;
041 import javax.management.RuntimeOperationsException;
042
043 import org.apache.qpid.management.Messages;
044 import org.apache.qpid.management.Names;
045 import org.apache.qpid.management.domain.model.type.Binary;
046 import org.apache.qpid.management.jmx.EntityLifecycleNotification;
047 import org.apache.qpid.transport.codec.BBDecoder;
048
049 /**
050 * Qpid event definition.
051 */
052 class QpidEvent extends QpidEntity implements QpidEventMBean
053 {
054
055 /**
056 * State interface for this event definition.
057 * Each state is responsible to handle the injection of the data and / or schema.
058 */
059 interface State
060 {
061 /**
062 * Adds the given data for the object instance associated to the given object identifier.
063 *
064 * @param rawData the raw configuration data.
065 */
066 void addNewEventData (byte[] rawData, long currentTimestamp, int severity);
067
068 /**
069 * Inject the schema into this class definition.
070 *
071 * @param propertyDefinitions
072 * @param statisticDefinitions
073 * @param methodDefinitions
074 * @throws UnableToBuildFeatureException when it's not possibile to parse schema and build the class definition.
075 */
076 public void setSchema (List<Map<String, Object>> agumentDefinitions) throws UnableToBuildFeatureException;
077 };
078
079
080 /**
081 * This is the initial state of every qpid class.
082 * The class definition instance is created but its schema has not been injected.
083 * Incoming configuration & instrumentation data will be stored in raw format because we don't know how to
084 * parse it until the schema arrives.
085 * In addition, this state is responsible (when data arrives) to request its schema.
086 */
087 final State _schemaNotRequested = new State() {
088
089 /**
090 * Stores the incoming data in raw format and request the schema for this class.
091 * After that a transition to the next state is made.
092 *
093 * @param objectId the object instance identifier.
094 * @param rawData incoming configuration data.
095 */
096 public synchronized void addNewEventData (byte[] rawData, long currentTimestamp, int severity)
097 {
098 try
099 {
100 requestSchema();
101 _state = _schemaRequestedButNotYetInjected;
102 } catch (Exception exception)
103 {
104 _logger.error(
105 exception,
106 Messages.QMAN_100015_UNABLE_TO_SEND_SCHEMA_REQUEST,
107 _parent.getName(),
108 _name);
109 } finally {
110 createEventInstance(rawData,currentTimestamp,severity);
111 }
112 }
113
114 /**
115 * This method only throws an illegal state exception because when a schema arrives
116 * this state is no longer valid.
117 */
118 public void setSchema (List<Map<String, Object>> agumentDefinitions) throws UnableToBuildFeatureException
119 {
120 throw new IllegalStateException("When a schema arrives it's not possible for this event to be in this state.");
121 }
122 };
123
124 /**
125 * This is the first state of this class definition : the schema is not yet injected so each injection of object data will be
126 * retained in raw format.
127 */
128 final State _schemaRequestedButNotYetInjected = new State()
129 {
130 /**
131 * Stores the incoming data in raw format and request the schema for this class.
132 * After that a transition to the next state is made.
133 *
134 * @param objectId the object instance identifier.
135 * @param rawData incoming configuration data.
136 */
137 public synchronized void addNewEventData (byte[] rawData,long currentTimestamp, int severity)
138 {
139 createEventInstance(rawData,currentTimestamp, severity);
140 }
141
142 /**
143 * When a schema is injected into this defintiion the following should happen :
144 * 1) the incoming schema is parsed and the class definition is built;
145 * 2) the retained raw data is converted into object instance(s)
146 * 3) the internal state of this class changes;
147 *
148 * If someting is wrong during that process the schema is not built and the state don't change.
149 */
150 public synchronized void setSchema (List<Map<String, Object>> argumentDefinitions) throws UnableToBuildFeatureException
151 {
152 MBeanAttributeInfo [] attributesMetadata = new MBeanAttributeInfo[argumentDefinitions.size()+2];
153
154 buildArguments(argumentDefinitions, attributesMetadata);
155
156 _metadata = new MBeanInfo(_name,_name,attributesMetadata,null,null,null);
157
158 // Converting stored object instances into JMX MBean and removing raw instance data.
159 for (QManManagedEvent instance : _eventInstances)
160 {
161 updateEventInstanceWithData(instance);
162 registerEventInstance(instance,_parent.getOwnerId(),_parent.getName(),_name);
163 }
164 _state = _schemaInjected;
165
166 EntityLifecycleNotification notification = new EntityLifecycleNotification(
167 EntityLifecycleNotification.SCHEMA_INJECTED_NOTIFICATION_TYPE,
168 _parent.getName(),
169 _name,
170 Names.EVENT,
171 _objectName);
172
173 sendNotification(notification);
174 }
175 };
176
177 /**
178 * After a schema is built into this definition this is the current state of the class.
179 */
180 final State _schemaInjected = new State()
181 {
182 /**
183 * Updates the configuration state of the object instance associates with the given object identifier.
184 *
185 * @param objectId the object identifier.
186 * @param rawData the configuration data (raw format).
187 */
188 public void addNewEventData (byte[] rawData,long currentTimestamp, int severity)
189 {
190 QManManagedEvent instance = createEventInstance(rawData,currentTimestamp, severity);
191 updateEventInstanceWithData(instance);
192 registerEventInstance(instance,_parent.getOwnerId(),_parent.getName(),_name);
193 }
194
195 /**
196 * Never called when the class definition has this state.
197 */
198 public void setSchema (List<Map<String, Object>> agumentDefinitions) throws UnableToBuildFeatureException
199 {
200 // N.A. : Schema is already injected.
201 }
202 };
203
204 /**
205 * MBean used for representing remote broker object instances.
206 * This is the core component of the QMan domain model
207 *
208 * @author Andrea Gazzarini
209 */
210 class QManManagedEvent extends QManManagedEntity
211 {
212
213
214 // Arrays used for storing raw data before this mbean is registered to mbean server.
215 final byte[] _rawEventData;
216 final long _timestamp;
217 final int _severity;
218
219 /**
220 * Builds a new managed object with the given object identifier.
221 *
222 * @param objectId the object identifier.
223 */
224 private QManManagedEvent(byte [] data, long timestamp, int severity)
225 {
226 this._rawEventData = data;
227 this._timestamp = timestamp;
228 this._severity = severity;
229 _attributes.put(SEVERITY_ATTR_NAME, _severity);
230 _attributes.put(TIMESTAMP_ATTR_NAME, new Date(_timestamp));
231 }
232
233 /**
234 * Returns the value of the given attribute.s
235 *
236 * @throws AttributeNotFoundException when no attribute is found with the given name.
237 */
238 public Object getAttribute (String attributeName) throws AttributeNotFoundException, MBeanException, ReflectionException
239 {
240 if (attributeName == null)
241 {
242 throw new RuntimeOperationsException(new IllegalArgumentException("Attribute name must not be null."));
243 }
244
245 if (_arguments.containsKey(attributeName) || SEVERITY_ATTR_NAME.equals(attributeName) || TIMESTAMP_ATTR_NAME.equals(attributeName))
246 {
247 return _attributes.get(attributeName);
248 } else
249 {
250 throw new AttributeNotFoundException(attributeName);
251 }
252 }
253
254 /**
255 * Executes an operation on this object instance.
256 *
257 * @param actionName the name of the method.
258 * @param params the method parameters
259 * @param signature the method signature.
260 */
261 public Object invoke (String actionName, Object[] params, String[] signature) throws MBeanException,ReflectionException
262 {
263 throw new ReflectionException(new NoSuchMethodException(actionName));
264 }
265
266 /**
267 * Sets the value of the given attribute on this object instance.
268 *
269 * @param attribute contains the new value of the attribute.
270 * @throws AttributeNotFoundException when the given attribute is not found on this object instance.
271 * @throws InvalidAttributeValueException when the given value is violating one attribute invariant.
272 */
273 public void setAttribute (Attribute attribute) throws AttributeNotFoundException,
274 InvalidAttributeValueException, MBeanException, ReflectionException
275 {
276 throw new ReflectionException(new NoSuchMethodException());
277 }
278
279 /**
280 * Sets the values of several attributes of this MBean.
281 *
282 * @param attributes a list of attributes: The identification of the attributes to be set and the values they are to be set to.
283 * @return The list of attributes that were set, with their new values.
284 */
285 public AttributeList setAttributes (AttributeList attributes)
286 {
287 throw new RuntimeException();
288 }
289 }
290
291 final static String SEVERITY_ATTR_NAME = "Severity";
292 final static String TIMESTAMP_ATTR_NAME = "Date";
293
294 private List<QpidProperty> _schemaOrderedArguments = new ArrayList<QpidProperty>();
295
296 Map<String, QpidProperty> _arguments = new HashMap<String, QpidProperty>();
297 List<QManManagedEvent> _eventInstances = new LinkedList<QManManagedEvent>();
298 State _state = _schemaNotRequested;;
299
300 /**
301 * Builds a new class with the given name and package as parent.
302 *
303 * @param className the name of the class.
304 * @param hash the class schema hash.
305 * @param parentPackage the parent of this class.
306 */
307 QpidEvent(String eventClassName, Binary hash, QpidPackage parentPackage)
308 {
309 super(eventClassName,hash,parentPackage,Names.EVENT);
310 }
311
312 /**
313 * Adds the configuration data for the object instance associated to the given object identifier.
314 *
315 * @param objectId the object identifier.
316 * @param rawData the raw configuration data.
317 */
318 void addEventData (byte[] rawData, long currentTimestamp, int severity)
319 {
320 _logger.debug(
321 Messages.QMAN_200021_INCOMING_EVENT_DATA,
322 _parent.getOwnerId(),
323 _parent.getName(),
324 _name);
325 _state.addNewEventData(rawData, currentTimestamp, severity);
326 }
327
328 /**
329 * Sets the schema for this class definition.
330 * A schema is basically a metadata description of all properties, statistics, methods and events of this class.
331 *
332 * @param propertyDefinitions properties metadata.
333 * @param statisticDefinitions statistics metadata.
334 * @param methodDefinitions methods metadata.
335 * @throws UnableToBuildFeatureException when some error occurs while parsing the incoming schema.
336 */
337 void setSchema (List<Map<String, Object>> argumentDefinitions) throws UnableToBuildFeatureException
338 {
339 _logger.info(Messages.QMAN_000010_INCOMING_SCHEMA,_parent.getOwnerId(),_parent.getName(),_name);
340 _state.setSchema(argumentDefinitions);
341 }
342
343 /**
344 * Internal method used for building attributes definitions.
345 *
346 * @param props the map contained in the properties schema.
347 * @param stats the map contained in the statistics schema.
348 * @param attributes the management metadata for attributes.
349 * @throws UnableToBuildFeatureException when it's not possibile to build one attribute definition.
350 */
351 void buildArguments (
352 List<Map<String, Object>> arguments,MBeanAttributeInfo[] attributes) throws UnableToBuildFeatureException
353 {
354 int index = 0;
355
356 for (Map<String, Object> argumentDefinition : arguments)
357 {
358 // Force metadata attributes. It is needed because arguments are "similar" to properties but they
359 // aren't properties and then they haven't optional, index and access metadata attributes
360 // (mandatory for build a property definition).
361 argumentDefinition.put(QpidFeatureBuilder.Attribute.optional.name(),0);
362 argumentDefinition.put(QpidFeatureBuilder.Attribute.index.name(),1);
363 argumentDefinition.put(QpidFeatureBuilder.Attribute.access.name(),3);
364
365 QpidFeatureBuilder builder = QpidFeatureBuilder.createPropertyBuilder(argumentDefinition);
366 builder.build();
367
368 QpidProperty argument = (QpidProperty) builder.getQpidFeature();
369
370 _arguments.put(argument.getName(),argument);
371 _schemaOrderedArguments.add(argument);
372 attributes[index++]=(MBeanAttributeInfo) builder.getManagementFeature();
373
374 _logger.debug(
375 Messages.QMAN_200019_EVENT_ARGUMENT_DEFINITION_HAS_BEEN_BUILT,
376 _parent.getName(),
377 _name,
378 argument);
379 }
380
381 attributes[index++] = new MBeanAttributeInfo(
382 SEVERITY_ATTR_NAME,
383 Integer.class.getName(),
384 Messages.EVENT_SEVERITY_ATTRIBUTE_DESCRIPTION,
385 true,
386 false,
387 false);
388
389 attributes[index++] = new MBeanAttributeInfo(
390 TIMESTAMP_ATTR_NAME,
391 Date.class.getName(),
392 Messages.EVENT_TIMESTAMP_ATTRIBUTE_DESCRIPTION,
393 true,
394 false,
395 false);
396 }
397
398 /**
399 * Returns the object instance associated to the given identifier.
400 * Note that if the identifier is not associated to any obejct instance, a new one will be created.
401 *
402 * @param objectId the object identifier.
403 * @param registration a flag indicating whenever the (new ) instance must be registered with MBean server.
404 * @return the object instance associated to the given identifier.
405 */
406 QManManagedEvent createEventInstance(byte [] data, long timestamp, int severity)
407 {
408 QManManagedEvent eventInstance = new QManManagedEvent(data, timestamp, severity);
409 _eventInstances.add(eventInstance);
410 return eventInstance;
411 }
412
413 /**
414 * Updates the given obejct instance with the given incoming configuration data.
415 *
416 * @param instance the managed object instance.
417 * @param rawData the incoming configuration data which contains new values for instance properties.
418 */
419 void updateEventInstanceWithData(QManManagedEvent instance)
420 {
421 BBDecoder decoder = new BBDecoder();
422 decoder.init(ByteBuffer.wrap(instance._rawEventData));
423
424 for (QpidProperty property : _schemaOrderedArguments)
425 {
426 try {
427 Object value = property.decodeValue(decoder);
428 instance.createOrReplaceAttributeValue(property.getName(),value);
429 } catch(Exception ignore) {
430 _logger.error(Messages.QMAN_100016_UNABLE_TO_DECODE_FEATURE_VALUE, _parent.getName(),_name,property.getName());
431 }
432 }
433 }
434
435 @Override
436 public String toString ()
437 {
438 return new StringBuilder()
439 .append(_parent.getOwnerId())
440 .append("::")
441 .append(_parent.getName())
442 .append(".")
443 .append(_name)
444 .toString();
445 }
446
447 /**
448 * Deregisters all the object instances and release all previously acquired resources.
449 */
450 void releaseResources ()
451 {
452 _eventInstances.clear();
453 JMX_SERVICE.unregisterEvents();
454 JMX_SERVICE.unregisterClassDefinitions();
455 _service.close();
456 }
457
458 /**
459 * Checks if this event definition contains event instance(s).
460 *
461 * @return true if there is one or more managed instances.
462 */
463 boolean hasNoInstances()
464 {
465 return _eventInstances.isEmpty();
466 }
467
468 /**
469 * Compose method used for registering an mbean (event) instance.
470 *
471 * @param instance the mbean event.
472 * @param brokerId the broker identifier.
473 * @param packageName the package name.
474 * @param eventClassName the event class name.
475 */
476 private void registerEventInstance(
477 QManManagedEvent instance,
478 UUID brokerId,
479 String packageName,
480 String eventClassName)
481 {
482 ObjectName objectName = JMX_SERVICE.registerEventInstance(instance,brokerId,packageName,eventClassName);
483
484 EntityLifecycleNotification notification = new EntityLifecycleNotification(
485 EntityLifecycleNotification.INSTANCE_ADDED_NOTIFICATION_TYPE,
486 packageName,
487 eventClassName,
488 Names.EVENT,
489 objectName);
490
491 sendNotification(notification);
492 }
493 }
|