/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2009
*
* 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.nc;
import alma.ACSErrTypeCommon.wrappers.AcsJCouldntPerformActionEx;
import alma.ACSErrTypeCommon.wrappers.AcsJIllegalStateEventEx;
import alma.acsErrTypeLifeCycle.wrappers.AcsJEventSubscriptionEx;
import alma.acsnc.EventDescription;
/**
* This interface provides an event subscriber
* that can be implemented on top of Corba NC or DDS, in-memory for local testing,
* or hopefully also on top of other technologies such as ZeroMQ.
* <p>
* Subscriber lifecycle:
* <ul>
* <li> Creation of the subscriber object may already allocate remote resources.
* Therefore you <b>must always call {@link #disconnect()} when done using the subscriber</b>,
* even if {@link #startReceivingEvents()} was never called.
* <li> Subscriptions by event type or for all types of events can be set up
* using {@link #addSubscription(Callback)} and {@link #addGenericSubscription(GenericCallback)}. <br>
* Subscriptions can optionally be removed again using {@link #removeSubscription(Class)}
* and {@link #removeGenericSubscription()}.
* TODO: Verify that these can be called both before and after startReceivingEvents. <br>
* <li> Call {@link #startReceivingEvents()} to actually subscribe.
* <li> Call {@link #suspend()} and {@link #resume()} to temporarily suspend and later resume
* the subscription. Depending on the underlying framework, suspending will typically
* cause events to be queued somewhere.
* <li> Call {@link #disconnect()} to destroy all resources. The subscriber cannot be used afterwards.
* </ul>
* The lifecycle is internally implemented using a state machine, see {@link #getLifecycleState()}.
* <p>
* Discussion:
* <ul>
* <li> One AcsEventSubscriber object may subscribe to different types of events,
* which for a Corba NC is normal, but for DDS requires tricks with topics and partitions.
* We might change this API to allow only one event type per NC.
* <li> TODO: We need to check if this API actually works for DDS, by integrating Rodrigo's RTI prototype.
* <li> For historical reasons the API is asymmetric with respect to creation and destruction of resources,
* because resources are allocated both in the ctor and in {@link #startReceivingEvents()},
* whereas they are released only in the single method {@link #disconnect()}.
* However, the underlying state machine knows two 'connect' and two 'disconnect' transitions
* and artificially maps them to the asymmetric API.
* If we want to make the API symmetric, we could break up 'disconnect' into 'stopReceivingEvents'
* and 'destroy'.
* </ul>
* @param <T> The event (base) type. If all events are of the same type then that type should be used; otherwise a common
* base type for all applicable events should be used, such as <code>Object</code> or <code>IDLEntity</code>.
*
* @author jslopez, hsommer
*/
public interface AcsEventSubscriber<T> {
/*===========================================*/
/* Subscription management methods */
/*===========================================*/
/**
* Adds a handler that will receive events of a specific type. The event type
* is determined by the value returned by calling {@link Callback#getEventType()}
* on the <code>receiver</code> parameter.
* <p>
* Note that the same event type can only be subscribed to with one handler,
* which means that another handler added for the same type will replace the previous handler.
* <p>
* The event type must be that of actual events, not of base classes.
* For example, the AcsEventSubscriber could be parameterized with base type <code>IDLEntity</code>
* and we could then add two subscriptions for events defined as IDL structs,
* say AntennaStatus and TemperatureData.
* If you want to subscribe to events without knowing their exact type,
* use {@link #addGenericSubscription(GenericCallback)} instead.
*
* @param receiver The callback to use when receiving events for the specified type.
*
* @throws AcsJEventSubscriptionEx If there is a problem and the receiver cannot be added
*/
public <U extends T> void addSubscription(Callback<U> receiver)
throws AcsJEventSubscriptionEx;
/**
* Removes the subscription for a specified event type or for all events types, so that the handler previously
* registered for that event type will no longer receive events.
*
* @param structClass
* the event type to be unsubscribed. If <code>null</code>, then all subscriptions but the generic
* subscription are removed.
*
* @throws AcsJEventSubscriptionEx
* if the specified event type has not been previously subscribed or if the removal fails with a
* technical problem.
*/
public <U extends T> void removeSubscription(Class<U> structClass)
throws AcsJEventSubscriptionEx;
/**
* Adds a generic handler for all types of events.
* This should only be used by monitoring tools and other special applications.
* For normal applications, use one or many calls to {@link #addSubscription(Callback)},
* each with a Callback object that matches exactly the subscribed event type.
* <p>
* It is possible to add a generic handler in addition to event type-specific handlers
* (where the latter will get precedence).
* Adding another generic handler will replace the previous generic handler.
*
* @param receiver The callback to use when receiving events
*
* @throws AcsJEventSubscriptionEx If there is a problem and the generic receiver cannot
* be added
*/
public void addGenericSubscription(GenericCallback receiver)
throws AcsJEventSubscriptionEx;
/**
* Removes the generic event handler, so that it will no longer receive events.
* Event specific handlers may still receive events.
*
* @throws SubscriptionNotFoundException If a generic receiver has not been previously subscribed
* @throws AcsJEventSubscriptionEx If there is any problem while unsubscribing the generic receiver
*/
public void removeGenericSubscription()
throws AcsJEventSubscriptionEx;
/*===========================================*/
/* Lifecycle methods */
/*===========================================*/
/**
* Returns the lifecycle state as obtained from an internal state machine.
* <p>
* This state can be used only for debug output.
* <b>The state names may change over time and should not be used to control execution.</b>
*/
public String getLifecycleState();
/**
* This method must be called to actually start receiving events.
* Typically it is called after the subscriptions are set up.
* User may still add and remove subscriptions at any given time, though.
* Also, the connection can be suspended and resumed.
* <p>
* No further invocations should be attempted on this method after one
* has been already successful. Otherwise, an {@link AcsJIllegalStateEventEx}
* will be thrown.
* <p>
* <b>If this method is not called, no event will ever be received</b>
*
* @throws AcsJIllegalStateEventEx If the user calls this method on an object that
* is already receiving events
* @throws AcsJCouldntPerformActionEx If any error happens while trying
* to start receiving events
*/
public void startReceivingEvents() throws AcsJIllegalStateEventEx, AcsJCouldntPerformActionEx;
/**
* Disconnects this subscriber from the Notification Channel, and releases all
* the resources associated with it. After this call, all registered handlers
* will stop receiving events, and this subscriber becomes unusable.
* <p>
* Calling this method over a subscriber object that has been already disconnected
* will throw an {@link AcsJIllegalStateEventEx}.
* <p>
* @throws AcsJIllegalStateEventEx If this method is called on an AcsEventSubscriber object
* that has been already disconnected
*/
public void disconnect() throws AcsJIllegalStateEventEx, AcsJCouldntPerformActionEx;
/**
* Used to temporarily halt receiving events of all types.
* <p>
* If the Subscriber has been connected already (method {@link #startReceivingEvents()},
* then after calling this method, incoming events will be buffered instead of being discarded;
* unexpired events will be received later, after a call to {@link #resume()}.
*
* <p>
// * This call has no effect if the Subscriber is not connected, or if it is
// * connected but already suspended.
*
* @throws AcsJIllegalStateEventEx if the subscriber is not connected to an NC.
*/
public void suspend() throws AcsJIllegalStateEventEx, AcsJCouldntPerformActionEx;
/**
* Returns <code>true</code> if this subscriber has been suspended.
*/
public boolean isSuspended();
/**
* Used to reenable the Subscriber after a call to the
* <code>suspend()</code> method. Queued events will be received after
* this call, see {@link #suspend()}.
*
* This call has no effect if the Subscriber is not connected, or if it is
* connected and already processing events.
*/
public void resume() throws AcsJIllegalStateEventEx, AcsJCouldntPerformActionEx;
/**
* User callback for received events.
* <p>
* This ACS-defined interface replaces the runtime search for the old
* "Consumer#receive(...)" method that was based on Java introspection.
*/
public static interface Callback<U> {
/**
* Event delivery, from the framework to user code.
* @param eventData The event data, e.g. an IDL-defined struct.
* @param eventDescrip Event meta data.
*/
public void receive(U eventData, EventDescription eventDescrip);
/**
* This method is needed for adding event-specific subscriptions
* and for the type-safety of this API (based on java generics),
* and should be implemented like
* <pre>
* public Class<MyEvent> getEventType() {
* return MyEvent.class;
* }
* </pre>
*/
public Class<U> getEventType();
}
/**
* Generic event callback, for use with {@link AcsEventSubscriber#addGenericSubscription(GenericCallback)}.
*/
public static interface GenericCallback {
/**
* TODO: Use T instead of Object ?
*/
public void receiveGeneric(Object event, EventDescription eventDescrip);
}
}