/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2002
* 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.container;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.omg.PortableServer.Servant;
import si.ijs.maci.ComponentSpec;
import alma.ACS.OffShoot;
import alma.ACS.OffShootOperations;
import alma.JavaContainerError.wrappers.AcsJContainerServicesEx;
import alma.acs.alarmsystem.source.AlarmSource;
import alma.acs.component.ComponentDescriptor;
import alma.acs.component.ComponentQueryDescriptor;
import alma.acs.component.ComponentStateManager;
import alma.entities.commonentity.EntityT;
import alma.maciErrType.wrappers.AcsJComponentDeactivationFailedEx;
import alma.maciErrType.wrappers.AcsJComponentDeactivationUncleanEx;
/**
* Through this interface, the container offers services to its hosted components.
* The component must call these methods explicitly; in this respect,
* <code>ContainerServices</code> is different from the other services that the container
* provides without the component implementation knowing about it.
* It can be thought of as a callback handle or a library.
* <p>
* Currently, methods are added to this interface as the functionality becomes available.
* At some point we will have to declutter the interface by introducing 2nd-level interfaces
* that harbor cohesive functionality. For example, instead of calling
* <code>myContainerServices.getComponent(...)</code>, the new call will then be something like
* <code>myContainerServices.communication().getComponent(...)</code>.
* <p>
* created on Oct 24, 2002 12:56:36 PM
* @author hsommer
*/
public interface ContainerServices extends ContainerServicesBase
{
/////////////////////////////////////////////////////////////
// basic needs (the rest comes from the base class)
/////////////////////////////////////////////////////////////
/**
* Delivers the <code>ComponentStateManager</code> object
* through which the component and the container administrate the
* state of the component.
* <p>
* The component needs to access the <code>ComponentStateManager</code>
* if it wishes to change its state.
* If it doesn't, only the container will change the state based on
* the information it has available.
*
* @return the state manager
* @see alma.ACS.ComponentStates
*/
public ComponentStateManager getComponentStateManager();
/////////////////////////////////////////////////////////////
// access to other components
/////////////////////////////////////////////////////////////
/**
* Gets a component specified by its instance name.
* Delegates to {@link si.ijs.maci.ManagerOperations#get_component(int, java.lang.String, boolean, org.omg.CORBA.IntHolder) get_component}.
* <ul>
* <li> This method is necessarily generic and will require a cast to the
* requested interface, like in <code>HelloComponent helloComp =
* HelloComponentHelper.narrow(containerServices.getComponent("HELLOCOMP1"));</code>.
*
* <li> the more general method <code>get_service</code> offered by the manager
* is deliberately not represented here. It would currently (Oct.03) offer
* access to "LogFactory", "NotifyEventChannelFactory", "ArchivingChannel@ARCHIVING.channels",
* "LoggingChannel", "InterfaceRepository", "CDB", "ACSLogSvc";
* It seems that if such access is needed, specialized methods should be added
* to this interface, like {@link #getCDB()}.
*
* <li> if requested, we may come up with some additional way
* (e.g. a code-generated singleton class)
* to give type safe access to other components, like done with EJB xxxHome.
*
* <li> the Container will later shortcut calls for components inside the same process.
* Like in EJB and CORBA, the implementation must therefore not assume receiving a
* by-value copy of any parameter from the other component.
* </ul>
*
* @param componentUrl the ACS CURL of the deployed component instance.
* @return the CORBA proxy for the component.
* @throws AcsJContainerServicesEx if something goes wrong.
*/
public org.omg.CORBA.Object getComponent(String componentUrl)
throws AcsJContainerServicesEx;
/**
* Gets a non-sticky reference to a component.
* This is typically used by "weak clients" such as graphical user interfaces that only want to observe the running system
* without interfering with its functioning.
* <p>
* A non-sticky reference does not bind the Manager to keep alive the Component, and the Client requesting a non-sticky reference
* is not considered when checking for reference counts.
* The non-sticky reference should not be released, as that call will fail.
* The Manager can deactivate Components independently of any non-sticky reference.
* Since a non-sticky reference is not considered in reference counting, it will also not activate the component if it is
* not already active. As a consequence, asking for a non-sticky reference to a not-active Component throws an exception.
* The client represented by id (the handle) must have adequate access rights to access the component.
* @param curl the component URL (component instance name)
* @return the CORBA proxy for the component.
* @throws AcsJContainerServicesEx if something goes wrong
* @since ACS 6.0
*/
public org.omg.CORBA.Object getComponentNonSticky(String curl)
throws AcsJContainerServicesEx;
/**
* Gets the default component specified by the component type.
* The type is the IDL type, such as <code>IDL:alma/PS/PowerSupply:1.0</code>.
* <p>
* A default instance for the given type must have been declared beforehand
* (either statically in the CDB config files, or dynamically),
* otherwise an exception is thrown.
* <p>
* To get more information on the returned component, call
* {@link #getComponentDescriptor} with the instance name that
* you can get from the component using {@link alma.ACS.ACSComponentOperations#name}.
* <p>
* Delegates to {@link si.ijs.maci.ManagerOperations#get_default_component}.
* @param componentIDLType
* @return
* @throws AcsJContainerServicesEx
*/
public org.omg.CORBA.Object getDefaultComponent(String componentIDLType)
throws AcsJContainerServicesEx;
/**
* Gets a component that will run collocated with a given component.
* @param compUrl the component's name (URL)
* @param targetCompUrl the name (URL) of the target component, in whose container we also want <code>compUrl</code> to run.
* @return the component reference, which should be cast using the appropriate CORBA narrow operation. Never null.
* @throws AcsJContainerServicesEx If the call failed and no component reference could be obtained.
* @since ACS 5.0.3
*/
public org.omg.CORBA.Object getCollocatedComponent(String compUrl, String targetCompUrl) throws AcsJContainerServicesEx;
/**
* Dynamic version of {@link #getCollocatedComponent(String, String)}.
* @param compSpec the description of the component to be created
* @param markAsDefaul if true, the new component will become the default component for its IDL type.
* @param targetCompUrl targetCompUrl the name (URL) of the target component, in whose container we also want <code>compUrl</code> to run.
* @return
* @throws AcsJContainerServicesEx If the call failed and no component reference could be obtained.
* @since ACS 6.0.4
*/
public org.omg.CORBA.Object getCollocatedComponent(ComponentQueryDescriptor compSpec, boolean markAsDefaul, String targetCompUrl) throws AcsJContainerServicesEx;
/**
* Gets a component whose instance is not registered in the CDB
* at deployment time.
* <p>
* The fields of <code>compSpec</code> can be filled in at various levels of detail,
* allowing the manager to blend in missing data from static deployment information.
* <b>For a detailed description of the various options,
* please refer to the ACS documentation.</b>
* <p>
* To get more information on the returned component, call
* {@link #getComponentDescriptor} with the instance name that
* you've specified or that you can get from the component in case it was
* assigned (see {@link alma.ACS.ACSComponentOperations#name()}).
* <p>
* Delegates to {@link si.ijs.maci.ManagerOperations#get_dynamic_component}.
*
* @param compSpec the description of the component to be created
* @param markAsDefault if true, the new component will become the default component for its IDL type.
*/
public org.omg.CORBA.Object getDynamicComponent(ComponentQueryDescriptor compSpec, boolean markAsDefault)
throws AcsJContainerServicesEx;
/**
* More powerful and thus more dangerous version of {@link #getDynamicComponent(ComponentQueryDescriptor, boolean)}
* which exposes a CORBA struct directly.
*
* @param compSpec the description of the component to be created
* @param markAsDefault if true, the new component will become the default component for its IDL type.
* @deprecated use {@link #getDynamicComponent(ComponentQueryDescriptor, boolean)} instead.
* if you need to set not only the standard fields <code>component_name</code> or <code>component_type</code>,
* but also the more advanced values <code>component_code</code> or <code>container_name</code>,
* please send a request to ACS so that access to these fields be included in the
* <code>ComponentQueryDescriptor</code> given to the recommended version of this method.
*/
public org.omg.CORBA.Object getDynamicComponent(ComponentSpec compSpec, boolean markAsDefault)
throws AcsJContainerServicesEx;
/**
* Finds components by their instance name (curl) and/or by their type.
* Wildcards can be used for the curl and type.
* This method returns a possibly empty array of component curls;
* for each curl, you may then call {@link #getComponent(String) getComponent}
* to obtain the reference.
*
* @param curlWildcard (<code>null</code> is understood as "*")
* @param typeWildcard (<code>null</code> is understood as "*")
* @return the curls of the component(s) that match the search.
* @see si.ijs.maci.ManagerOperations#get_component_info(int, int[], java.lang.String, java.lang.String, boolean)
*/
public String[] findComponents(String curlWildcard, String typeWildcard)
throws AcsJContainerServicesEx;
/**
* Gets the component descriptor which contains meta data for the component.
* (Not to be confused with a component descriptor in the EJB sense.)
* <p>
* To be used primarily after retrieval of a component with
* {@link #getDefaultComponent(String) getDefaultComponent}
* or {@link #getDynamicComponent(ComponentQueryDescriptor, boolean) getDynamicComponent},
* when some data like the instance name is not known.
* The idea behind having this method separately is that in many cases,
* components are not interested in this information, and are happier to
* get back from these methods directly the remote reference to another component,
* instead of always dealing with a <code>ComponentDescriptor</code>.
*
* @param componentUrl the unique name of a component
* @return the descriptor from which the various data items can be obtained.
* @throws ContainerException if <code>componentUrl</code> is unknown
* or anything else goes wrong
* @see si.ijs.maci.ComponentInfo
*/
public ComponentDescriptor getComponentDescriptor(String componentUrl)
throws AcsJContainerServicesEx;
/**
* Releases the specified component. This involves notification of the manager,
* as well as calling <code>_release()</code> on the CORBA stub for that component.
* If the curl is not known to the container, the request will be ignored.
* <p>
* This method will return only when the component has actually been released,
* which may take some time in case there are still active requests being processed.
* <p>
* This method is kept for convenience, providing a specific subset of the functionality that
* the more flexible {@link #releaseComponent(String, ComponentReleaseCallback)} offers.
* It blocks the client until the component is released or a timeout of 60 seconds has
* occurred, and logs all errors of component release at level DEBUG.
*
* @param componentUrl the name/curl of the component instance as used by the manager
*/
public void releaseComponent(String componentUrl);
/**
* Callback that can be optionally given to
* {@link ContainerServices#releaseComponent(String, ComponentReleaseCallback)}.
* Users may override the methods they need in their subclasses of <code>ComponentReleaseCallback</code>.
* <p>
* Note that {@link #awaitComponentRelease(long, TimeUnit)} is not a callback method
* but should make it easier to synchronize user code execution with component release.
* <p>
* An instance of <code>ComponentReleaseCallback</code> can be used for only one component release call.
*/
public static class ComponentReleaseCallback {
private CountDownLatch sync = new CountDownLatch(1);
/**
* Called by ACS to release the thread (if any) that blocks on {@link #awaitComponentRelease(long, TimeUnit)}.
*/
final void callOver() {
sync.countDown();
}
/**
* Called when a CORBA or other communication error occurred.
*/
public void errorCommunicationFailure(Throwable thr) {}
/**
* Called when the client cannot legally release the component, e.g. because it no longer holds a reference to it.
*/
public void errorNoPermission(String reason) {}
/**
* Called when the component reference has been successfully released.
* @param deactivationUncleanEx If the component was deactivated with problems, this exception will be forwarded; otherwise <code>null</code> for clean deactivation.
*/
public void componentReleased(AcsJComponentDeactivationUncleanEx deactivationUncleanEx) {}
/**
* Called when the target component deactivation failed.
* @param deactivationFailureEx to provide details about the failure.
*/
public void errorComponentReleaseFailed(AcsJComponentDeactivationFailedEx deactivationFailureEx) {}
/**
* This is not a callback method but a convenience method to "park" the calling thread
* until
* <ul>
* <li>The component has been released, or
* <li>the given timeout has struck, or
* <li>component release failed with an exception.
* </ul>
* A client that only wants to wait for component release without caring about the details
* does not have to subclass <code>ComponentReleaseCallback</code>,
* but can simply call <code>awaitComponentRelease</code>.
* The client may in addition override the callback methods though.
*
* @param timeout The maximum time to wait for the component release to succeed.
* @param unit The unit of <code>timeout</code>.
* @return <code>true</code> if the component was released properly, <code>false</code> if the call returns because of a timeout.
* See {@link CountDownLatch#await(long, TimeUnit)}.
* @throws InterruptedException
*/
public final boolean awaitComponentRelease(long timeout, TimeUnit unit) throws InterruptedException {
return sync.await(timeout, unit);
}
}
/**
* Variant of {@link ComponentReleaseCallback} which logs all errors.
*/
public static class ComponentReleaseCallbackWithLogging extends ComponentReleaseCallback {
private final Logger logger;
private Level level;
public ComponentReleaseCallbackWithLogging(Logger logger, Level level) {
this.logger = logger;
this.level = level;
}
public void errorCommunicationFailure(Throwable thr) {
logger.log(level, "errorCommunicationFailure: ", thr);
}
public void errorNoPermission(String reason) {
logger.log(level, "errorNoPermission: " + reason);
}
public void componentReleased(AcsJComponentDeactivationUncleanEx deactivationUncleanEx) {
logger.log(level, "componentReleased: ", deactivationUncleanEx);
}
public void errorComponentReleaseFailed(AcsJComponentDeactivationFailedEx deactivationFailureEx) {
logger.log(level, "errorComponentReleaseFailed: ", deactivationFailureEx);
}
}
/**
* Releases the reference to the specified component.
* Releasing a component reference may result in the deactivation of that component,
* if no other clients hold (sticky) references.
* <p>
* This call will return as soon as the release request has been delivered to the ACS manager,
* which means that the reference count evaluation and possible component deactivation will happen
* only after returning from this call.
* <p>
* If your code must synchronize with the reference evaluation and possible component deactivation in the target container,
* or if you are interested in exceptions that may occur during component deactivation,
* then you should supply the optional <code>ComponentRequestCallback</code> object and block execution of your
* calling thread until you receive the callback.
* <p>
* If an exception (such as no-permission) is thrown during the synchronous first part
* of the underlying call to the manager, then this exception will not be thrown upward by this method
* but will instead be reported to the optional callback object, just like any
* exception that happens later during the asynchronous part of the component release.
* The idea here is to have either "interested" clients that want to get all exceptions,
* or "easy" clients that do not care about any exceptions, thus do not provide a callback object,
* and also do not want to bother about a try/catch block.
*
* @param componentUrl the name/curl of the component instance as used by the manager
* @param callback may be <code>null</code> if you do not need to wait for component deactivation or to see the results.
* An new instance of <code>ComponentReleaseCallback</code> is required for every call.
* @since ACS 9.1
*/
public void releaseComponent(String componentUrl, ComponentReleaseCallback callback);
public static interface ComponentListener {
/**
* Called to find out whether a filter should be applied
* such that only notifications arrive for components to which the caller holds a reference.
*/
boolean includeForeignComponents();
/**
* Called when components become available
*/
void componentsAvailable(List<ComponentDescriptor> comps);
/**
* Called when components become unavailable
*/
void componentsUnavailable(List<String> compNames);
}
/**
* Allows a client to register a callback object that gets notified when some
* component(s) in use by the client (= components the client requested previously)
* dies or comes back to life (with <code>ComponentListener#includeForeignComponents()==false</code>).
* <p>
* If the client wants to get notified even for components that it does not hold a reference to,
* then <code>ComponentListener#includeForeignComponents()</code> should return <code>true</code>;
* notification about components that this client does not use may be limited though, e.g.
* to components collocated in the same container.
*
* @param listener
* @see si.ijs.maci.ClientOperations#components_available(si.ijs.maci.ComponentInfo[])
* @since ACS 6.0
*/
public void registerComponentListener(ComponentListener listener);
/**
* Wraps a component reference (or offshoot reference etc) such that the given timeout is applied on
* the client side of calls to this (possibly remote) object.
* If the total call, including de-/serialization and network travel, takes longer than the given timeout,
* an <code>org.omg.CORBA.TIMEOUT</code> exception will be thrown.
* <p>
* This allows us to override the general timeout given at the system level (e.g. orb.properties file in case of jacorb)
* and the container-level timeout given in the CDB container configuration.
* It is possible to set the timeout to values that are shorter or longer than the default timeout.
* You should chose a timeout value that matches the expected response time, with a large safety margin of
*
* <p>
* <b>Note that calls to which the specified timeout should apply must be made on the object reference returned from this method,
* and not on the original object that gets passed to this method!
* Some corba implementations may apply the timeout to both objects though, or return the original object.</b>
* <p>
*
* @param corbaRef Reference to a component or an offshoot as obtained from some of the other container services methods.
* @param timeoutSeconds the custom client side timeout in seconds, to be used for all calls to the given object reference.
* @return A new object reference which should be used to make calls with the specified timeout applied.
*/
public org.omg.CORBA.Object getReferenceWithCustomClientSideTimeout(org.omg.CORBA.Object originalCorbaRef, double timeoutSeconds)
throws AcsJContainerServicesEx;
/////////////////////////////////////////////////////////////
// support for XML entities and binding classes
/////////////////////////////////////////////////////////////
/**
* Creates a unique id and sets it on the given (binding class) entity object.
* The id will be redundantly stored in an encrypted format so that later it can be verified that
* the id is indeed the one originally assigned.
*
* @param entity must be freshly created and
*/
public void assignUniqueEntityId(EntityT entity) throws AcsJContainerServicesEx;
/**
* Converts a "flat-XML" remote object (RO) interface
* (as obtained from the various <code>getComponent</code> methods, in the
* case of components, or by retrieving offshoots from components,
* in the case of offshoots) to a "transparent-XML" RO interface.
* This is only applicable to ROs that contain XML entities
* in their IDL interface methods, and for which the build process
* has been set up to generate XML binding classes and the "transparent-XML"
* interface in addition to the standard Java-IDL compiler output.
* <p>
* In the case that the remote object is a component, the container can
* fulfill this request in two different ways:
* <ol>
* <li><b>Remote component</b>:
* if the specified component runs in a different container,
* a dynamic wrapper object will be created around the provided CORBA stub.
* The wrapper object will translate between entity object parameters
* in the form of serialized XML (as found in <code>flatXmlIF</code>)
* and the corresponding Java binding classes (in <code>transparentXmlIF</code>).
* <li><b>Collocated component</b>:
* if the specified component runs in the same container, the container
* may choose to shortcut the (de-) serialization of XML binding classes.
* In this case, Java binding classes are transported in memory,
* and all communication with the other component is done without CORBA.
* </ol>
* In either case, the returned <code>Object</code> implements the
* <code>transparentXmlIF</code> interface.
* The client component that calls this method should only cast to that interface,
* and does not need to know which of the two transport mechanisms are being used.
*
* @param transparentXmlIF remote object interface with XML binding classes.
* @param objectReference reference to the remote object to be wrapped, which
* implements <code>flatXmlIF</code>.
* Currently supported remote objects are ACS Components and OffShoots.
* @param flatXmlIF remote object interface ("xxxOperations") where entity objects are represented as
* serialized XML inside a CORBA struct.
* @return the object that implements <code>transparentXmlIF</code>.
*/
public <T, F> T getTransparentXmlWrapper(
Class<T> transparentXmlIF, F objectReference, Class<F> flatXmlIF)
throws AcsJContainerServicesEx;
/**
* Activates an Object as an OffShoot.
* <p>
* In contrast to the old {@link #activateOffShoot(Servant)} method, which always expects a
* Servant (either subclass of xyzPOA skeleton or a xyzPOATie instance), this method is more flexible and
* receives two arguments:
* <ul>
* <li>The object implementing {@link alma.ACS.OffShootOperations}, but without
* restricting it to be a Servant, and </li>
* <li>The operations interface that this object directly implements.</li>
* </ul>
* This way, we support
* <ul>
* <li>Activation of "normal" OffShoots: <br>
* <code>offshootImpl</code> should be a corba Servant, either a subclass of the
* XyzPOA skeleton, or a XyzPOATie instance (delegation model). <br>
* <code>idlOpInterface</code> should be <code>XyzOperations.class</code>.
* </li>
* <li>OffShoots featuring automatic XML (de-)serialization, similar to what has always been offered for ACS Components. <br>
* <code>offshootImpl</code> should be an object that implements the generated <code>XyzJ</code> interface
* (which uses XML binding classes and extends <code>OffShootOperations</code>).
* This object is not expected to be a Corba Servant.<br>
* <code>idlOpInterface</code> should be <code>XyzJ.class</code>.
* </ul>
* Since ACS 9.0, when giving an OffShoot with XML entities (de-)serialization, automatic
* interception code is created to wrap the given object, and a XyzPOATie instance is
* created automatically and used as the servant.
*
* Since ACS 4.1.2, a tie servant is detected, and interception code gets inserted between the
* POATie skeleton and the offshoot implementation class. This way, the container
* can intercept (and log) calls to offshoots in the same way as it does for calls to components.
* <b>It is therefore recommended to use the tie approach for all offshoot servants,
* unless there is a reason to avoid container interception.</b>
* <p>
* @param <T> The type of <code>offshootImpl</code>, one of the <code>xyzJ</code> or
* <code>xyzOperations</code> interfaces
* @param offshootImpl the object that implements the OffShoot logic
* @param idlOpInterface the IDL operations interface implemented by <code>offshootImpl</code>;
* currently, must be one of <code>xyzOperations</code> or <code>xyzJ</code>
* @return The Corba-activated offshoot object. Needs a narrow-cast to the subtype, like
* <code>CBdouble myCBdouble = alma.ACS.CBdoubleHelper.narrow(...)</code>.
* @throws AcsJContainerServicesEx if anything goes wrong,
* especially if <code>offshootImpl</code> is does not implement {@link alma.ACS.OffShootOperations}.
* @since ACS 9.0
*/
<T extends OffShootOperations> OffShoot activateOffShoot(T offshootImpl, Class<T> idlOpInterface)
throws AcsJContainerServicesEx;
/////////////////////////////////////////////////////////////
// Alarm Service API
/////////////////////////////////////////////////////////////
/**
* Returns the {@link AlarmSource} owned by this object. The {@link AlarmSource} object
* allows to raise and clear alarms, among other advanced operations
* <p>
* @TODO: We could remove the AcsJContainerServicesEx declaration, it is currently not needed.
*
* @return The {@link AlarmSource} owned by this object
*/
public AlarmSource getAlarmSource();
/////////////////////////////////////////////////////////////
// other
/////////////////////////////////////////////////////////////
}