/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2004
* Copyright by ESO (in the framework of the ALMA collaboration),
* All rights reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package alma.ACS.MasterComponentImpl;
import java.util.logging.Logger;
import alma.ACS.CBDescIn;
import alma.ACS.CBDescOut;
import alma.ACS.CBstringSeq;
import alma.ACS.CBstringSeqHelper;
import alma.ACS.CBstringSeqPOA;
import alma.ACS.MonitorstringSeq;
import alma.ACS.OffShoot;
import alma.ACS.ROstringSeq;
import alma.ACSErr.ACSErrTypeOK;
import alma.ACSErr.Completion;
import alma.ACSErr.CompletionHolder;
import alma.ACSErrTypeCommon.wrappers.AcsJIllegalStateEventEx;
import alma.ACSErrTypeOK.ACSErrOK;
import alma.acs.container.ContainerServices;
import alma.acs.exceptions.AcsJCompletion;
import alma.acs.genfw.runtime.sm.AcsStateUtil;
/**
* Convenience implementation of a subsystem state change listener.
* Encapsulates the activation as a callback object, the registration as a monitor of the
* master component state property, and the reception of state change notifications.
*
* Can be subclassed (see {@link #stateChangedNotification(String[])}).
*
* @author hsommer
* created Apr 30, 2004 11:08:27 AM
*/
public class StateChangeListener extends CBstringSeqPOA
{
private ROstringSeq statesProperty;
private MonitorstringSeq monitor;
private ContainerServices contSrv;
private final MyStateChangeSemaphore stateChangeSemaphore;
protected final Logger logger;
public StateChangeListener(Logger logger)
{
this.logger = logger;
stateChangeSemaphore = new MyStateChangeSemaphore();
}
private class MyStateChangeSemaphore extends StateChangeSemaphore
{
public MyStateChangeSemaphore() {
super(logger);
}
synchronized void externalStateChangedNotify() {
stateChangedNotify();
}
}
/**
* Creates a semaphore that can be used to wait for a given number of state changes.
* Useful to wait with sending the next event until a previous action state has finished
* its /do method, and moved on to the next state.
*/
public StateChangeSemaphore getStateChangeSemaphore()
{
return stateChangeSemaphore;
}
/**
* Creates a monitor for <code>statesProperty</code> from this instance.
*
* @param statesProperty
* @param contSrv
* @return usually not needed
* @throws Exception
*/
public MonitorstringSeq createMonitor(ROstringSeq statesProperty, ContainerServices contSrv) throws Exception
{
this.contSrv = contSrv;
if (monitor != null) {
destroyMonitor();
}
this.statesProperty = statesProperty;
// register this callback object with CORBA
OffShoot offshoot = contSrv.activateOffShoot(this);
CBstringSeq cbStringSeq = CBstringSeqHelper.narrow(offshoot);
// register callback CORBA object with the statesProperty as a monitor
monitor = statesProperty.create_monitor(cbStringSeq, new CBDescIn());
// baci.idl: "On creation, the only trigger present will be the timer trigger.
// Calling the set_value_trigger method determines the behaviour of the value trigger.
// The enable parameter determines whether the value trigger is active or not ."
// Strings have no triggerable value, thus don't care
monitor.set_value_trigger(new String[0], true);
// baci.idl: "Timer trigger can be disabled by passing the value 0 for timer parameter."
monitor.set_timer_trigger(0);
return monitor;
}
/**
*
* @throws Exception
*/
public void destroyMonitor() throws Exception
{
if (monitor != null) {
monitor.destroy();
monitor = null;
contSrv.deactivateOffShoot(this);
}
}
/**
* @see alma.ACS.CBstringSeqOperations#working(java.lang.String[], alma.ACSErr.Completion, alma.ACS.CBDescOut)
*/
public final void working(String[] value, Completion completion, CBDescOut desc)
{
stateChangeSemaphore.externalStateChangedNotify();
stateChangedNotification(value);
}
/**
* Subclasses may override this and do something useful with the new state...
* @param newState
*/
protected void stateChangedNotification(String[] newStateHierarchy)
{
logNotification(newStateHierarchy, null);
}
/**
* @see alma.ACS.CBstringSeqOperations#done(java.lang.String[], alma.ACSErr.Completion, alma.ACS.CBDescOut)
*/
public void done(String[] value, Completion completion, CBDescOut desc)
{
logger.fine("Component property 'hierarchicalState' has been released (callback method 'done' was called).");
}
/**
* Logs a state change notification.
* @param value the new state hierarchy
* @param completion an optional completion
*/
protected void logNotification(String[] value, Completion completion)
{
String msg = "hierarchical state = '";
for (int i = 0; i < value.length; i++) {
msg += value[i];
if (i < value.length -1) {
msg += '/';
}
}
msg += "'. ";
if (completion != null) {
msg += "Completion=";
AcsJCompletion compl = AcsJCompletion.fromCorbaCompletion(completion);
if (compl.isError()) {
msg += compl.getAcsJException().toString();
}
else {
msg += "ok";
}
}
logger.finer(msg);
}
/**
* @see alma.ACS.CallbackOperations#negotiate(long, alma.ACS.CBDescOut)
*/
public boolean negotiate(long timeout, CBDescOut desc)
{
return false;
}
/**
* Reads the current state hierarchy.
* @return State hierarchy with outmost state first
* @throws AcsJIllegalStateEventEx if the state can't be read ; @TODO: use better fitting ex (don't want to create one now right before the release)
*/
public String[] getCurrentState() throws AcsJIllegalStateEventEx {
CompletionHolder ch = new CompletionHolder();
String[] statesHierarchy = statesProperty.get_sync(ch);
AcsJCompletion statesSyncCompletion = AcsJCompletion.fromCorbaCompletion(ch.value);
if (statesSyncCompletion.isError() ||
statesSyncCompletion.getType() != ACSErrTypeOK.value ||
statesSyncCompletion.getCode() != ACSErrOK.value ||
statesHierarchy == null ) {
throw new AcsJIllegalStateEventEx("Failed to retrieve current subsystem state.");
}
return statesHierarchy;
}
/**
* Helper method for the repeated task of getting the current state hierarchy and
* comparing it against the expected hierarchy.
* @return true if the current state hierarchy is equal to <code>expectedHierarchy</code>.
*/
public boolean verifyCurrentState(String[] expectedHierarchy)
{
boolean ret = false;
String[] actualHierarchy = null;
try {
actualHierarchy = getCurrentState();
String expectedPath = AcsStateUtil.stateHierarchyNamesToString(expectedHierarchy);
String actualPath = AcsStateUtil.stateHierarchyNamesToString(actualHierarchy);
if (actualPath.equals(expectedPath)) {
ret = true;
//logger.info("current state hierarchy '" + actualPath + "' as expected");
}
else {
logger.info("current state hierarchy '" + actualPath + "' differs from expected '" + expectedPath + "'.");
}
}
catch (Exception ex) {
; // ret=false
}
return ret;
}
}