/*
* Copyright (c) 2007, Fraunhofer-Gesellschaft
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* (1) Redistributions of source code must retain the above copyright
* notice, this list of conditions and the disclaimer at the end.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* (2) Neither the name of Fraunhofer nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* DISCLAIMER
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package org.ogf.graap.wsag.server.monitoring;
import java.text.MessageFormat;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;
import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlBoolean;
import org.apache.xmlbeans.XmlInt;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlString;
import org.ogf.graap.wsag.api.Agreement;
import org.ogf.graap.wsag.api.logging.LogMessage;
import org.ogf.graap.wsag.server.accounting.IAccountingSystem;
import org.ogf.graap.wsag.server.accounting.SimpleAccountingSystemLogger;
import org.ogf.graap.wsag.server.api.IAgreementContext;
import org.ogf.graap.wsag.server.api.impl.AgreementContext;
import org.ogf.graap.wsag.server.persistence.impl.PersistentAgreementContainer;
import org.ogf.schemas.graap.wsAgreement.AgreementContextType;
import org.ogf.schemas.graap.wsAgreement.AgreementPropertiesType;
import org.ogf.schemas.graap.wsAgreement.AgreementStateType;
import org.ogf.schemas.graap.wsAgreement.GuaranteeTermStateType;
import org.ogf.schemas.graap.wsAgreement.ServiceTermStateType;
import org.ogf.schemas.graap.wsAgreement.TermTreeType;
import org.ogf.schemas.graap.wsAgreement.TerminateInputType;
import org.quartz.CronExpression;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
/**
* MonitorableAgreement
*
* Supports monitoring of service terms, agreement state and automatic evaluation of guarantee terms.
*
* @author Oliver Waeldrich
*
*/
public class MonitorableAgreement extends Observable
implements Agreement, Observer
{
private static final Logger LOG = Logger.getLogger( MonitorableAgreement.class );
//
// definition of constants set in the execution properties
//
/**
* Key to resolve if monitoring was started for a particular agreement implementation from the agreement
* execution context. It refers a {@link XmlBoolean}. If the boolean value is true monitoring was started
* using the {@link MonitorableAgreement}, if false or not present monitoring was not used.
*
* Used for loading persisted agreements.
*
* @see #getAgreementInstance()
* @see Agreement#getExecutionContext()
*/
public static final String MONITORING_ACTIVE = "org.wsag4j.monitoring.isActive";
/**
* Key to resolve the monitoring interval for a particular agreement implementation from the agreement
* execution context. It refers a {@link XmlString} value.
*
* Used for loading persisted agreements.
*
* @see #getAgreementInstance()
* @see Agreement#getExecutionContext()
*/
public static final String MONITORING_CRON = "org.wsag4j.monitoring.cron";
/**
* Key to resolve the class names of the monitoring handler for a particular agreement implementation from
* the agreement execution context. It refers a {@link XmlString}. The monitoring handler class names are
* stored in the agreement execution context with the following strategy:
*
* <code>MONITORING_HANDLER + "." + handler[i].getClass().getName()</code>
*
* Used for loading persisted agreements.
*
* @see #getAgreementInstance()
* @see Agreement#getExecutionContext()
*/
public static final String MONITORING_HANDLER = "org.wsag4j.monitoring.handler";
/**
* Key to resolve the number of the monitoring handler for a particular agreement implementation from the
* agreement execution context. It refers a {@link XmlInt}.
*
* Used for loading persisted agreements.
*
* @see #getAgreementInstance()
* @see Agreement#getExecutionContext()
*/
public static final String MONITORING_HANDLER_COUNT = "org.wsag4j.monitoring.handler.count";
//
// private variable definitions
//
private Agreement agreementInstance;
private IAgreementContext executionContext = new AgreementContext( this );
private final List<IServiceTermMonitoringHandler> monitoringHandler =
new Vector<IServiceTermMonitoringHandler>();
private Scheduler scheduler;
private String jobName;
private static final String JOB_GROUP = "WSAG4J";
private IAccountingSystem accountingSystem = new SimpleAccountingSystemLogger();
private boolean monitoring = false;
/**
* @return the monitoring
*/
public boolean isMonitoring()
{
return monitoring;
}
//
// default schedule for monitoring that fires each minute
//
private static final String DEFAULT_SCHEDULE = "0 0/1 * * * ?";
private String cronExpression = DEFAULT_SCHEDULE;
/**
* Creates a new instance of a monitorable agreement. The agreement object, for which this
* MonitorableAgreement is created, implements the methods to store the terms and the state of the
* agreement, and to terminate the agreement.
*
* @param agreement
* the agreement object, which should be monitored.
*/
public MonitorableAgreement( Agreement agreement )
{
this.agreementInstance = agreement;
//
// register this instance as observer to state change events of the agreement instance
//
agreement.addObserver( this );
//
// update the execution context
//
executionContext.getExecutionProperties().putAll( agreement.getExecutionContext() );
executionContext.getTransientExecutionProperties().putAll( agreement.getTransientExecutionContext() );
initializeScheduler();
}
/**
* Recreates an instance of a monitorable agreement. The agreement object, for which this
* MonitorableAgreement is created, implements the methods to store the terms and the state of the
* agreement, and to terminate the agreement.
*
* @param persistentAgreementContainer
* the persisted agreement object, which should be monitored.
*/
public MonitorableAgreement( PersistentAgreementContainer persistentAgreementContainer )
{
// agreement.addObserver(this);
throw new UnsupportedOperationException( "Not supported yet." );
}
private void initializeScheduler()
{
//
// Initialize the quartz scheduler
//
synchronized ( SchedulerFactory.class )
{
SchedulerFactory factory = new StdSchedulerFactory();
try
{
scheduler = factory.getScheduler();
if ( !scheduler.isStarted() )
{
scheduler.start();
}
}
catch ( SchedulerException e )
{
throw new IllegalStateException( "Failed to instantiate Quartz scheduler.", e );
}
}
}
private IMonitoringContext initializeMonitoringContext()
{
IMonitoringContext monitoringContext = new MonitoringContext();
//
// add the execution context properties to the monitoring context
//
monitoringContext.setProperties( executionContext.getExecutionProperties() );
//
// add the transient execution context properties to the monitoring context
//
monitoringContext.setTransientProperties( executionContext.getTransientExecutionProperties() );
// deprecated
// //
// // add the agreement execution context
// //
// monitoringContext.getProperties().putAll(executionContext.getExecutionProperties());
//
// add monitoring handler to monitoring context
//
monitoringContext.setMonitoringHandler( new IServiceTermMonitoringHandler[0] );
for ( int i = 0; i < monitoringHandler.size(); i++ )
{
monitoringContext.addMonitoringHandler( monitoringHandler.get( i ) );
}
monitoringContext.setAccountingSystem( accountingSystem );
//
// put the agreement context into the transient properties
//
monitoringContext.getTransientProperties().put(
IMonitoringContext.WSAG4J_AGREEMENT_EXECUTION_CONTEXT, getExecutionContext() );
return monitoringContext;
}
private synchronized void scheduleMonitoringJobs( IMonitoringContext monitoringContext ) throws Exception
{
//
// TODO: set properties isMonitoring in the execution context
// this allows us to restart monitoring if an agreement
// was reloaded
//
jobName = initializeJobName();
Trigger agreementMonitoringTrigger = createCronTrigger( jobName );
//
// create job details
//
JobDetail agreementMonitoringDetail = new JobDetail( jobName, JOB_GROUP, AgreementMonitorJob.class );
agreementMonitoringDetail.getJobDataMap().put( AgreementMonitorJob.WSAG4J_AGREEMENT_INSTANCE,
agreementInstance );
agreementMonitoringDetail.getJobDataMap().put( AgreementMonitorJob.WSAG4J_MONITORING_CONTEXT,
monitoringContext );
scheduler.scheduleJob( agreementMonitoringDetail, agreementMonitoringTrigger );
//
// TODO: schedule job for terminating the monitoring process
// if AgreementState is completed
//
}
private String initializeJobName() throws SchedulerException
{
List<String> names = Arrays.asList( scheduler.getJobNames( JOB_GROUP ) );
String name =
MessageFormat.format( "WSAG4J_MONITORING_JOB_{0}", new Object[] { new Date().getTime() } );
while ( names.contains( name ) )
{
name = MessageFormat.format( "WSAG4J_MONITORING_JOB_{0}", new Object[] { new Date().getTime() } );
try
{
wait( 10 );
}
catch ( InterruptedException e )
{
// do nothing, just continue
}
}
return name;
}
/**
* Stores the number and class names of the monitoring handler in the execution properties.
*/
private void saveHandlerToExecutionContext()
{
int handlerCount = 0;
Iterator<IServiceTermMonitoringHandler> handler = monitoringHandler.iterator();
while ( handler.hasNext() )
{
String handlerClass = handler.next().getClass().getName();
String key = MONITORING_HANDLER + "." + handlerCount;
XmlString value = XmlString.Factory.newValue( handlerClass );
getMonitoringContext().getExecutionProperties().put( key, value );
handlerCount++;
}
XmlInt value = XmlInt.Factory.newValue( Integer.valueOf( handlerCount ) );
getMonitoringContext().getExecutionProperties().put( MONITORING_HANDLER_COUNT, value );
}
/**
* Initializes the monitoring handler based on the handler count and names stored in the execution
* context.
*/
private void initializeHandlerFromExecutionContext()
{
int handlerCount = 0;
monitoringHandler.clear();
XmlInt count =
(XmlInt) getMonitoringContext().getExecutionProperties().get( MONITORING_HANDLER_COUNT );
if ( count != null )
{
handlerCount = count.getIntValue();
}
while ( handlerCount > 0 )
{
handlerCount--;
String key = MONITORING_HANDLER + "." + handlerCount;
XmlString value = (XmlString) getMonitoringContext().getExecutionProperties().get( key );
LOG.debug( "initialize agreement monitoring handler" );
//
// load the agreement monitoring handler
//
try
{
String className = value.getStringValue();
LOG.debug( LogMessage.getMessage( "instantiate monitoring handler ''{0}''", className ) );
Class<IServiceTermMonitoringHandler> clazz;
try
{
//
// check if the class to instantiate implements our handler interface
//
@SuppressWarnings( "unchecked" )
Class<IServiceTermMonitoringHandler> convert =
(Class<IServiceTermMonitoringHandler>) this.getClass().getClassLoader()
.loadClass( className );
clazz = convert;
}
catch ( ClassCastException e )
{
final String msgNotRequiredInterface =
"monitoring handler must implement the 'IServiceTermMonitoringHandler' interface.";
throw new Exception( msgNotRequiredInterface, e );
}
//
// instantiate and add the monitoring handler
//
addMonitoringHandler( clazz.newInstance() );
LOG.debug( LogMessage.getMessage( "successfully instantiated monitoring handler ''{0}''",
className ) );
}
catch ( Exception e )
{
String msgText = "re-initializing monitorable agreement failed: {0}";
LogMessage message = LogMessage.getMessage( msgText, e.getMessage() );
LOG.error( message, e );
}
}
}
/**
* @return the executionContext
* @deprecated
*/
@Deprecated
public IAgreementContext getMonitoringContext()
{
return executionContext;
}
/**
* @param executionContext
* the executionContext to set
*/
public void setExecutionContext( IAgreementContext executionContext )
{
this.executionContext = executionContext;
}
/**
*
* @param handler
* monitoring handler
*/
public void addMonitoringHandler( IServiceTermMonitoringHandler handler )
{
monitoringHandler.add( handler );
}
/**
* Returns the list of registered monitoring handler.
*
* @return the monitoringHandler
*/
public IServiceTermMonitoringHandler[] getMonitoringHandler()
{
return monitoringHandler.toArray( new IServiceTermMonitoringHandler[monitoringHandler.size()] );
}
/**
* @return the cronExpression
*/
public String getCronExpression()
{
return cronExpression;
}
/**
* @param cronExpression
* the cronExpression to set
*/
public void setCronExpression( String cronExpression )
{
this.cronExpression = cronExpression;
}
private Trigger createCronTrigger( String name ) throws Exception
{
//
// create the cron trigger for job monitoring
//
CronTrigger trigger = new CronTrigger();
try
{
if ( CronExpression.isValidExpression( cronExpression ) )
{
trigger.setCronExpression( cronExpression );
}
else
{
LOG.error( LogMessage.getMessage(
"Invalid cron expression ({0}). Using default monitoring schedule ({1}).",
cronExpression, DEFAULT_SCHEDULE ) );
trigger.setCronExpression( DEFAULT_SCHEDULE );
}
}
catch ( ParseException e )
{
String msgText = "Invalid default schedule <{0}>. Monitoring not scheduled.";
String message = LogMessage.format( msgText, DEFAULT_SCHEDULE );
throw new Exception( message, e );
}
trigger.setGroup( JOB_GROUP );
trigger.setName( name );
return trigger;
}
/**
* Starts the agreement monitoring process.
*
* @throws Exception
* failed to start monitoring
*/
public void startMonitoring() throws Exception
{
//
// set flag that the monitoring process has started
//
XmlBoolean isMonitoring = XmlBoolean.Factory.newValue( true );
XmlString cron = XmlString.Factory.newValue( cronExpression );
getMonitoringContext().getExecutionProperties().put( MONITORING_ACTIVE, isMonitoring );
getMonitoringContext().getExecutionProperties().put( MONITORING_CRON, cron );
//
// save the monitoring handler in the execution context
//
saveHandlerToExecutionContext();
//
// initialize the monitoring context
//
IMonitoringContext monitoringContext = initializeMonitoringContext();
//
// schedule the monitoring jobs
//
try
{
scheduleMonitoringJobs( monitoringContext );
}
catch ( Exception e )
{
final String msgText = "Error scheduling monitoring jobs. Reason: {0}";
String message = LogMessage.format( msgText, e.getMessage() );
LOG.error( message, e );
throw new Exception( message, e );
}
this.monitoring = true;
}
/**
* Stops the agreement monitoring.
*
* @throws Exception
* error while stopping the agreement monitor scheduler
*/
public void stopMonitoring() throws Exception
{
try
{
XmlBoolean isMonitoring = XmlBoolean.Factory.newValue( false );
getMonitoringContext().getExecutionProperties().put( MONITORING_ACTIVE, isMonitoring );
getAgreementInstance().getExecutionContext().put( MONITORING_ACTIVE, isMonitoring );
//
// TODO: unset properties isMonitoring in the execution context
// when an agreement is reloaded, the monitoring is started
// if isMonitoring property is set in the execution properties
//
scheduler.unscheduleJob( jobName, JOB_GROUP );
}
catch ( SchedulerException e )
{
String message = "Error stoping the agreement monitoring. Reason: " + e.getMessage();
LOG.error( message );
throw new Exception( message, e );
}
this.monitoring = false;
}
/**
* {@inheritDoc}
*/
@Override
public void terminate( TerminateInputType reason )
{
try
{
stopMonitoring();
}
catch ( Exception ex )
{
LOG.error( "The agreement monitoring scheduler was not stoped" );
if ( LOG.isDebugEnabled() )
{
LOG.debug( ex );
}
}
try
{
agreementInstance.terminate( reason );
}
catch ( Exception ex )
{
LOG.error( "The agreement could not be terminated." );
if ( LOG.isDebugEnabled() )
{
LOG.debug( ex );
}
}
}
/**
* This method notifies a concrete agreement instance that the monitored agreement instance was reloaded.
* This essentially implies that the {@link Agreement#notifyReload(java.util.Map)} method is invoked.
* Implementations of the {@link Agreement} can override the
* {@link Agreement#notifyReinitialized(java.util.Map)} in order to implement domain specific
* re-initialization logic.
*
* @throws Exception
* indicates an error during the agreement reload process
*/
public void notifyReload() throws Exception
{
agreementInstance.notifyReload( executionContext.getExecutionProperties() );
//
// re-initialize monitoring handler and restart agreement monitoring
//
initializeHandlerFromExecutionContext();
Map<String, XmlObject> executionProperties = getMonitoringContext().getExecutionProperties();
XmlString cron = (XmlString) executionProperties.get( MonitorableAgreement.MONITORING_CRON );
if ( cron != null )
{
cronExpression = cron.getStringValue();
}
else
{
cronExpression = DEFAULT_SCHEDULE;
}
boolean isActive = false;
if ( executionProperties.containsKey( MONITORING_ACTIVE ) )
{
isActive =
( (XmlBoolean) executionProperties.get( MonitorableAgreement.MONITORING_ACTIVE ) ).getBooleanValue();
}
if ( isActive )
{
startMonitoring();
}
}
/**
* @return the agreement id
* @see org.ogf.graap.wsag.api.Agreement#getAgreementId()
*/
@Override
public String getAgreementId()
{
return agreementInstance.getAgreementId();
}
/**
* @return the agreement context
* @see org.ogf.graap.wsag.api.Agreement#getContext()
*/
@Override
public AgreementContextType getContext()
{
return agreementInstance.getContext();
}
/**
* @return the guarantee term states
* @see org.ogf.graap.wsag.api.Agreement#getGuaranteeTermStates()
*/
@Override
public GuaranteeTermStateType[] getGuaranteeTermStates()
{
return agreementInstance.getGuaranteeTermStates();
}
/**
* @return the agreement name
* @see org.ogf.graap.wsag.api.Agreement#getName()
*/
@Override
public String getName()
{
return agreementInstance.getName();
}
/**
* @return the service term states
* @see org.ogf.graap.wsag.api.Agreement#getServiceTermStates()
*/
@Override
public ServiceTermStateType[] getServiceTermStates()
{
return agreementInstance.getServiceTermStates();
}
/**
* @return the agreement state
* @see org.ogf.graap.wsag.api.Agreement#getState()
*/
@Override
public AgreementStateType getState()
{
return agreementInstance.getState();
}
/**
* @return the terms of the agreement
* @see org.ogf.graap.wsag.api.Agreement#getTerms()
*/
@Override
public TermTreeType getTerms()
{
return agreementInstance.getTerms();
}
/**
* {@inheritDoc}
*/
public Agreement getAgreementInstance()
{
return agreementInstance;
}
/**
* @param accountingSystem
* the accountingSystem to set
*/
public void setAccountingSystem( IAccountingSystem accountingSystem )
{
if ( accountingSystem != null )
{
this.accountingSystem = accountingSystem;
}
}
/**
* @return the accountingSystem
*/
public IAccountingSystem getAccountingSystem()
{
return accountingSystem;
}
/**
* If the monitored agreement receives a state change notification of the concrete agreement
* implementation (@link {@link Agreement#notifyObservers()}) all observer registered to this monitorable
* agreement will be notified of the state change ass well.
*
* {@inheritDoc}
*
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*/
@Override
public void update( Observable o, Object arg )
{
setChanged();
notifyObservers();
// if ( o instanceof Wsag4jObservable )
// {
// Wsag4jObservable observable = (Wsag4jObservable) o;
// if ( observable.getType() == agreementInstance )
// {
// setChanged();
// notifyObservers();
// }
// }
}
/**
* @see org.ogf.graap.wsag.api.Agreement#validate()
*/
@Override
public boolean validate()
{
return agreementInstance.validate();
}
/**
* @see org.ogf.graap.wsag.api.Agreement#notifyReload(java.util.Map)
*/
@Override
public void notifyReload( Map<String, XmlObject> executionCtx )
{
agreementInstance.notifyReload( executionCtx );
}
/**
* @see org.ogf.graap.wsag.api.Agreement#setAgreementId(java.lang.String)
*/
@Override
public void setAgreementId( String agreementId )
{
agreementInstance.setAgreementId( agreementId );
}
/**
* @see org.ogf.graap.wsag.api.Agreement#setContext(org.ogf.schemas.graap.wsAgreement.AgreementContextType)
*/
@Override
public void setContext( AgreementContextType context )
{
agreementInstance.setContext( context );
}
/**
* @see org.ogf.graap.wsag.api.Agreement#setName(java.lang.String)
*/
@Override
public void setName( String name )
{
agreementInstance.setName( name );
}
/**
* @see org.ogf.graap.wsag.api.Agreement#setTerms(org.ogf.schemas.graap.wsAgreement.TermTreeType)
*/
@Override
public void setTerms( TermTreeType terms )
{
agreementInstance.setTerms( terms );
}
/**
* @see org.ogf.graap.wsag.api.Agreement#setState(org.ogf.schemas.graap.wsAgreement.AgreementStateType)
*/
@Override
public void setState( AgreementStateType agreementState )
{
agreementInstance.setState( agreementState );
}
/**
* @see org.ogf.graap.wsag.api.Agreement#setGuaranteeTermStates(org.ogf.schemas.graap.wsAgreement.GuaranteeTermStateType[])
*/
@Override
public void setGuaranteeTermStates( GuaranteeTermStateType[] guaranteeTermStateList )
{
agreementInstance.setGuaranteeTermStates( guaranteeTermStateList );
}
/**
* @see org.ogf.graap.wsag.api.Agreement#setServiceTermStates(org.ogf.schemas.graap.wsAgreement.ServiceTermStateType[])
*/
@Override
public void setServiceTermStates( ServiceTermStateType[] serviceTermStateList )
{
agreementInstance.setServiceTermStates( serviceTermStateList );
}
/**
* @see org.ogf.graap.wsag.api.Agreement#getXMLObject()
*/
@Override
public AgreementPropertiesType getXMLObject()
{
return agreementInstance.getXMLObject();
}
/**
* @see org.ogf.graap.wsag.api.Agreement#setXmlObject(org.ogf.schemas.graap.wsAgreement.AgreementPropertiesType)
*/
@Override
public void setXmlObject( AgreementPropertiesType properties )
{
agreementInstance.setXmlObject( properties );
}
/**
* @see org.ogf.graap.wsag.api.Agreement#getTransientExecutionContext()
*/
@Override
public Map<String, Object> getTransientExecutionContext()
{
return agreementInstance.getTransientExecutionContext();
}
/**
* @see org.ogf.graap.wsag.api.Agreement#getImplementationClass()
*/
@Override
public Class getImplementationClass()
{
return agreementInstance.getImplementationClass();
}
/*
* (non-Javadoc)
*
* @see org.ogf.graap.wsag.api.Agreement#getExecutionContext()
*/
@Override
public Map<String, XmlObject> getExecutionContext()
{
return agreementInstance.getExecutionContext();
}
}