/*=============================================================================*
* Copyright 2006 The Apache Software Foundation
*
* Licensed 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.muse.ws.dm.muws.impl;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.xml.namespace.QName;
import org.w3c.dom.Element;
import org.apache.muse.util.xml.XmlUtils;
import org.apache.muse.ws.addressing.soap.SoapFault;
import org.apache.muse.ws.dm.muws.Metric;
import org.apache.muse.ws.dm.muws.Metrics;
import org.apache.muse.ws.dm.muws.MuwsConstants;
import org.apache.muse.ws.resource.WsResource;
import org.apache.muse.ws.resource.metadata.MetadataDescriptor;
import org.apache.muse.ws.resource.properties.ResourcePropertyCollection;
/**
*
* SimpleMetrics is Muse's default implementation of the WSDM Metrics
* capability. It uses the WS-RP and WS-RMD implementations in Muse to collect
* metric metadata and expose it to WS-RP clients without requiring users to
* treat metric and non-metric properties differently in their code. Metrics
* that are defined in a resource's RMD have various property change listeners
* and approvers set up for them so that all metadata is kept up-to-date.
*
* @author Dan Jemiolo (danj)
*
*/
public class SimpleMetrics
extends AbstractManageabilityCapability implements Metrics
{
//
// Collection of threads for counter metrics that run on an interval
//
private Map _countersByQName = new HashMap();
//
// All metrics defined for the resource
//
private Map _metricsByQName = new HashMap();
public Date getCurrentTime()
{
return new Date();
}
public Metric getMetric(QName propertyQName)
{
return (Metric)_metricsByQName.get(propertyQName);
}
public QName[] getPropertyNames()
{
return PROPERTIES;
}
public void initialize()
throws SoapFault
{
super.initialize();
WsResource resource = getWsResource();
ResourcePropertyCollection props = resource.getPropertyCollection();
MetadataDescriptor metadata = props.getMetadata();
//
// load all of the metric data from the RMD
//
Iterator i = metadata.getPropertyNames().iterator();
while (i.hasNext())
{
QName qname = (QName)i.next();
String capability = metadata.getExtendedMetadata(qname, MuwsConstants.CAPABILITY_QNAME);
//
// a property is a metric IF it defines a Capability element in
// its RMD definition with the Metrics capability URI
//
if (capability != null && capability.equals(MuwsConstants.METRICS_URI))
{
Metric metric = new SimpleMetric(qname, resource);
_metricsByQName.put(qname, metric);
//
// on demand? update metadata before reads
//
if (metric.isOnDemand())
props.addReadListener(new MetricOnDemandListener(metric));
//
// add metric attributes for all reads
//
props.addReadListener(new MetricDecorationProvider(metric));
//
// update metadata whenever changes occur
//
props.addChangeListener(new MetricUpdateListener(metric));
//
// special case: counters that run on an interval can be
// automated by starting a thread that increments the value
// every N seconds
//
if (metric.isCounter() && metric.isInterval())
{
Thread counter = new CounterThread(props, metric);
_countersByQName.put(qname, counter);
counter.start();
}
}
}
}
public void shutdown()
throws SoapFault
{
//
// make sure we kill off any counters
//
Iterator i = _countersByQName.values().iterator();
while (i.hasNext())
{
Thread next = (Thread)i.next();
next.destroy();
}
super.shutdown();
}
/**
*
* CounterThread simply updates an integer property value every N seconds,
* as specified in a metric's RMD definition. It checks to see if a metric
* has been reset before each update so it doesn't overwrite any resets.
*
* @author Dan Jemiolo (danj)
*
*/
class CounterThread extends Thread
{
private Metric _metric = null;
private ResourcePropertyCollection _properties = null;
public CounterThread(ResourcePropertyCollection properties, Metric metric)
{
_properties = properties;
_metric = metric;
}
private int getCurrentValue()
{
QName name = _metric.getName();
Element[] values = null;
try
{
values = _properties.getResourceProperty(name);
}
catch (SoapFault fault)
{
throw new RuntimeException(fault.getMessage(), fault);
}
return XmlUtils.getInteger(values[0]).intValue();
}
public void run()
{
QName name = _metric.getName();
long interval = _metric.getInterval();
int currentValue = getCurrentValue();
while (true)
{
try
{
Thread.currentThread().sleep(interval);
}
catch (InterruptedException error)
{
//
// just continue on - nothing we can do about inaccuracy
//
error.printStackTrace();
}
if (_metric.hasBeenReset())
currentValue = getCurrentValue();
try
{
Integer asObject = new Integer(++currentValue);
_properties.updateResourceProperty(name, new Object[]{ asObject });
}
catch (SoapFault fault)
{
//
// just continue on - what can we do?
//
fault.printStackTrace();
}
}
}
}
}