/*
* 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.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Vector;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.omg.CosNaming.NamingContext;
import org.omg.CosNaming.NamingContextHelper;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.Servant;
import com.cosylab.CDB.DAL;
import si.ijs.maci.ComponentInfo;
import si.ijs.maci.ComponentSpec;
import alma.ACS.CBlong;
import alma.ACS.OffShoot;
import alma.ACS.OffShootHelper;
import alma.ACS.OffShootOperations;
import alma.ACSErrTypeCommon.wrappers.AcsJBadParameterEx;
import alma.ACSErrTypeCommon.wrappers.AcsJCouldntPerformActionEx;
import alma.ACSErrTypeCommon.wrappers.AcsJIllegalStateEventEx;
import alma.ACSErrTypeCommon.wrappers.AcsJUnexpectedExceptionEx;
import alma.JavaContainerError.wrappers.AcsJContainerEx;
import alma.JavaContainerError.wrappers.AcsJContainerServicesEx;
import alma.acs.alarmsystem.source.AlarmSource;
import alma.acs.callbacks.RequesterUtil;
import alma.acs.callbacks.ResponseReceiver;
import alma.acs.component.ComponentDescriptor;
import alma.acs.component.ComponentQueryDescriptor;
import alma.acs.component.ComponentStateManager;
import alma.acs.component.dynwrapper.ComponentInvocationHandler;
import alma.acs.component.dynwrapper.DynamicProxyFactory;
import alma.acs.container.archive.Range;
import alma.acs.container.archive.UIDLibrary;
import alma.acs.container.corba.AcsCorba;
import alma.acs.exceptions.AcsJException;
import alma.acs.logging.AcsLogLevel;
import alma.acs.logging.AcsLogger;
import alma.acs.logging.ClientLogManager;
import alma.acs.nc.AcsEventPublisher;
import alma.acs.nc.AcsEventSubscriber;
import alma.alarmsystem.source.ACSAlarmSystemInterfaceFactory;
import alma.entities.commonentity.EntityT;
import alma.maciErrType.wrappers.AcsJComponentDeactivationFailedEx;
import alma.maciErrType.wrappers.AcsJComponentDeactivationUncleanEx;
import alma.maciErrType.wrappers.AcsJNoPermissionEx;
import alma.maciErrType.wrappers.AcsJmaciErrTypeEx;
import alma.xmlstore.Identifier;
import alma.xmlstore.IdentifierHelper;
import alma.xmlstore.IdentifierJ;
import alma.xmlstore.IdentifierOperations;
/**
* Implementation of the <code>ContainerServices</code> interface.
* To be used by ACS components, as well as any other clients that need access
* to components.
* <p>
* This class is "cheap" to instantiate because many resources it uses are singletons
* and or objects otherwise shared among instances.
* It should thus be ok to create one instance per component or other client.
* <p>
* This class has to be thread-safe, because a component's functional methods can be called from
* different ORB threads, or because the component could itself create threads, each of them
* accessing this object.
*
* @author hsommer Apr 1, 2003 2:28:01 PM
*/
public class ContainerServicesImpl implements ContainerServices
{
private AdvancedContainerServicesImpl advancedContainerServices;
// identifier archive and UID lib will created lazily
private volatile UIDLibrary uidLibrary;
private volatile IdentifierJ identifierArchive;
/** cheat property that allows testing without identifier archive present, because UIDs will be faked */
public static final String PROPERTYNAME_FAKE_UID_FOR_TESTING = "acs.container.fakeUIDsForTesting";
private final boolean fakeUIDsForTesting = Boolean.getBoolean(PROPERTYNAME_FAKE_UID_FOR_TESTING);
/**
* Holds and re-establishes the connection to the manager, and encapsulates the handle given by the manager at login.
*/
protected final AcsManagerProxy m_acsManagerProxy;
// logger used by this class
protected final AcsLogger m_logger;
// logger given to component
private volatile AcsLogger componentLogger;
// sync'd map, key=curl, value=corbaStub
private final Map<String, org.omg.CORBA.Object> m_usedComponentsMap;
private final Map<String, org.omg.CORBA.Object> m_usedNonStickyComponentsMap;
// sync'd map, key=curl, value=ComponentDescriptor
private final Map<String, ComponentDescriptor> m_componentDescriptorMap;
/**
* The handle that the manager has assigned to the component to whom this ContainerServices object belongs,
* or 0 if this ContainerServices object does not belong to a component,
* in which case m_acsManagerProxy's handle should be used.
*/
private final int m_componentHandle;
/**
* Name of the component or other client (client app or container etc)
*/
private final String m_clientName;
/**
* The externally provided instance of AcsCorba
*/
private final AcsCorba acsCorba;
private final POA m_clientPOA;
private Object m_componentXmlTranslatorProxy;
// sync'd map, key=offshot implementation object, value=servant
// (they can be the same, but help to keep track of the servants
// when activating offshoots of xyzJ type)
private Map<Object, Servant> m_activatedOffshootsMap;
private final ComponentStateManager m_componentStateManager;
private final ThreadFactory m_threadFactory;
private volatile String[] methodsExcludedFromInvocationLogging;
/**
* Optional callback object for component available/unavailable notification
*/
private ComponentListener compListener;
private final List<CleanUpCallback> cleanUpCallbacks;
private final Map<String, AcsEventSubscriber<?>> m_subscribers;
private final Map<String, AcsEventPublisher<?>> m_publishers;
/**
* Subscriber instance gets created using reflection, since module jcontnc must come after jcont for other reasons.
*/
private final String CLASSNAME_NC_SUBSCRIBER = "alma.acs.nc.NCSubscriber";
/**
* Publisher instance gets created using reflection, since module jcontnc must come after jcont for other reasons.
*/
private final String CLASSNAME_NC_PUBLISHER = "alma.acs.nc.NCPublisher";
/**
* Instead of using "synchronized" lazy factory methods, there is this separate sync object
* for lazy instantiations, to not conflict with possible other uses of this object's monitor.
*/
private final Object lazyCreationSync = new Object();
/**
* The cached CDB reference.
*/
private final DAL cdb;
/**
* ctor.
* @param acsManagerProxy
* @param componentPOA the POA for the component. Can be the root POA or some other specialized POA.
* @param acsCorba Encapsulates the ORB and all POAs
* @param logger logger to be used by this class
* @param componentHandle handle to be used for identification when sending requests to the manager.
* For components, this should be the component handle assigned by the manager;
* for other clients, it should be 0 to indicate that the handle obtained at manager login should be used.
* @param clientCurl
* @param componentStateManager can be null if this class is instantiated
* for a component client outside of a container
* @param threadFactory to be used for <code>getThreadFactory</code>
*/
public ContainerServicesImpl(AcsManagerProxy acsManagerProxy, DAL cdb, POA componentPOA, AcsCorba acsCorba,
AcsLogger logger, int componentHandle, String clientCurl,
ComponentStateManager componentStateManager,
ThreadFactory threadFactory)
{
// The following fields are final. This guarantees that they will be copied to main thread memory,
// and thus be seen by other threads after this ctor has terminated.
// For the sake of test dummy implementations, we do not assert that all params be != null,
// even though normally they must be.
m_acsManagerProxy = acsManagerProxy;
this.cdb = cdb;
m_clientPOA = componentPOA;
this.acsCorba = acsCorba;
m_logger = logger;
m_componentHandle = componentHandle;
m_clientName = clientCurl;
m_componentStateManager = componentStateManager;
// should do for thread-safety as long as we don't iterate over it
m_usedComponentsMap = Collections.synchronizedMap(new HashMap<String, org.omg.CORBA.Object>());
m_usedNonStickyComponentsMap = Collections.synchronizedMap(new HashMap<String, org.omg.CORBA.Object>());
m_componentDescriptorMap = Collections.synchronizedMap(new HashMap<String, ComponentDescriptor>());
m_activatedOffshootsMap = Collections.synchronizedMap(new HashMap<Object, Servant>());
m_subscribers = new HashMap<String, AcsEventSubscriber<?>>();
m_publishers = new HashMap<String, AcsEventPublisher<?>>();
m_threadFactory = threadFactory;
cleanUpCallbacks = new ArrayList<CleanUpCallback>();
if (fakeUIDsForTesting) {
m_logger.warning("Running in test mode where UIDs will be constructed randomly instead of being retrieved from the archive!");
}
}
void setComponentXmlTranslatorProxy(Object xmlTranslatorProxy) {
m_componentXmlTranslatorProxy = xmlTranslatorProxy;
}
/////////////////////////////////////////////////////////////
// Implementation of ContainerServices
/////////////////////////////////////////////////////////////
/**
* Gets the component name (which the component does not know statically)
* @see alma.acs.container.ContainerServices#getName()
*/
public String getName() {
return m_clientName;
}
/**
* {@inheritDoc}
*
* This method should only be called by a component that lives inside a container;
* a component client that is not a component itself should not call it,
* would result in a NPE!
*
* @see alma.acs.container.ContainerServices#getComponentStateManager()
*/
public ComponentStateManager getComponentStateManager()
{
if (m_componentStateManager == null)
{
// to make debugging easier if this ever happened on the container side
throw new NullPointerException("ComponentStateManager is null!");
}
return m_componentStateManager;
}
/**
* The component must retrieve its logger object from this interface
* (as opposed to using the <code>ClientLogManager</code> singleton)
* so that the container is free
* to give away loggers that are somehow tailored to the particular component.
* <p>
* The goal is to have "componentName" and other fields in all ALMA log entries,
* and have tool support for filtering logs by component, subsystem, user, ...
*
* @see alma.acs.container.ContainerServices#getLogger()
*/
public AcsLogger getLogger()
{
if (componentLogger == null) {
componentLogger = ClientLogManager.getAcsLogManager().getLoggerForComponent(m_clientName);
}
return componentLogger;
}
public void registerComponentListener(ComponentListener listener) {
compListener = listener;
}
/**
*/
public void fireComponentsAvailable (List<ComponentDescriptor> compDescs) {
if (compListener == null) {
return;
}
// find out which components are interesting for the client
List<ComponentDescriptor> interesting = null;
if (compListener.includeForeignComponents()) {
interesting = compDescs;
}
else {
interesting = new Vector<ComponentDescriptor>();
for (ComponentDescriptor cd : compDescs) {
if (m_usedComponentsMap.containsKey(cd.getName()) || m_usedNonStickyComponentsMap.containsKey(cd.getName())) {
interesting.add(cd);
}
}
}
if (interesting.size() > 0) {
try {
compListener.componentsAvailable(interesting);
} catch (Throwable thr) {
m_logger.log(Level.INFO, "componentsAvailable implementation of client " + m_clientName + " failed", thr);
}
}
}
/**
*/
public void fireComponentsUnavailable (List<String> compNames) {
if (compListener == null) {
return;
}
// find out which components are interesting for the client
List<String> interesting = null;
if (compListener.includeForeignComponents()) {
interesting = compNames;
}
else {
interesting = new Vector<String>();
for (String cn : compNames) {
if (m_usedComponentsMap.containsKey(cn) || m_usedNonStickyComponentsMap.containsKey(cn) ) {
interesting.add(cn);
}
}
}
if (interesting.size() > 0 && compListener != null) {
try {
compListener.componentsUnavailable(interesting);
} catch (Throwable thr) {
m_logger.log(Level.INFO, "componentsUnavailable implementation of client " + m_clientName + " failed", thr);
}
}
}
/**
* @see alma.acs.container.ContainerServices#assignUniqueEntityId(EntityT)
*/
@Override
public void assignUniqueEntityId(EntityT entity) throws AcsJContainerServicesEx
{
if (entity == null) {
AcsJBadParameterEx cause = new AcsJBadParameterEx();
cause.setParameter("entity");
cause.setParameterValue("null");
throw new AcsJContainerServicesEx(cause);
}
if (fakeUIDsForTesting) {
long localId = (new Random(System.currentTimeMillis())).nextLong();
String uid = Range.generateUID("testArchiveId", "testRangeId", localId);
entity.setEntityId(uid);
return;
}
try {
if (identifierArchive == null) {
Identifier identRaw = IdentifierHelper.narrow(getDefaultComponent("IDL:alma/xmlstore/Identifier:1.0"));
identifierArchive = getTransparentXmlWrapper(IdentifierJ.class, identRaw, IdentifierOperations.class);
}
if (uidLibrary == null) {
uidLibrary = new UIDLibrary(m_logger);
}
uidLibrary.assignUniqueEntityId(entity, identifierArchive);
}
catch (Throwable thr) {
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(thr);
String msg = "Failed to assign a UID to entity of type ";
if (entity.getEntityTypeName()!= null) {
msg += entity.getEntityTypeName();
}
else {
msg += entity.getClass().getSimpleName();
}
ex.setContextInfo(msg);
throw ex;
}
}
/**
* @see alma.acs.container.ContainerServices#findComponents(java.lang.String, java.lang.String)
*/
@Override
public String[] findComponents(String curlWildcard, String typeWildcard)
throws AcsJContainerServicesEx
{
if (curlWildcard == null) {
curlWildcard = "*";
}
if (typeWildcard == null) {
typeWildcard = "*";
}
String msgSpec = "curlWildcard='" + curlWildcard + "' and typeWildcard='" + typeWildcard + "'.";
if (m_logger.isLoggable(Level.FINER)) {
m_logger.finer("about to call Manager#get_component_info with " + msgSpec);
}
ComponentInfo[] components = null;
try {
components = m_acsManagerProxy.get_component_info(new int[0], curlWildcard, typeWildcard, false );
}
catch (AcsJNoPermissionEx ex) {
m_logger.log(Level.FINE, "No permission to find components with " + msgSpec, ex);
AcsJContainerServicesEx ex2 = new AcsJContainerServicesEx(ex);
ex2.setContextInfo(msgSpec);
throw ex2;
}
catch (Throwable thr) {
m_logger.log(Level.FINE, "Unexpected failure calling 'get_component_info' with " + msgSpec, thr);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(thr);
ex.setContextInfo(msgSpec);
throw ex;
}
ArrayList<String> curls = new ArrayList<String>();
if (components != null) {
for (int i = 0; i < components.length; i++) {
curls.add(components[i].name);
}
}
if (m_logger.isLoggable(Level.FINER)) {
m_logger.finer("received " + curls.size() + " curls from get_component_info.");
}
return curls.toArray(new String[curls.size()]);
}
/**
*
* @see alma.acs.container.ContainerServices#getComponentDescriptor(java.lang.String)
*/
public ComponentDescriptor getComponentDescriptor(String curl)
throws AcsJContainerServicesEx
{
if (curl == null) {
AcsJBadParameterEx cause = new AcsJBadParameterEx();
cause.setParameter("curl");
cause.setParameterValue("null");
throw new AcsJContainerServicesEx(cause);
}
ComponentDescriptor desc = m_componentDescriptorMap.get(curl);
if (desc == null) {
// try to get it from the manager
ComponentInfo[] compInfos;
try {
compInfos = m_acsManagerProxy.get_component_info(new int[0], curl, "*", false);
} catch (Throwable thr) {
m_logger.log(Level.FINE, "Unexpected failure calling 'get_component_info'.", thr);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(thr);
ex.setContextInfo("CURL=" + curl);
throw ex;
}
if (compInfos.length == 1) {
desc = new ComponentDescriptor(compInfos[0]);
m_componentDescriptorMap.put(curl, desc);
}
else {
String msg = "failed to retrieve a unique component descriptor for the component instance " + curl;
m_logger.fine(msg);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx();
ex.setContextInfo(msg);
throw new AcsJContainerServicesEx();
}
}
return desc;
}
/**
* @see alma.acs.container.ContainerServices#getComponent(String)
*/
public org.omg.CORBA.Object getComponent(String curl) throws AcsJContainerServicesEx
{
if (curl == null) {
AcsJBadParameterEx cause = new AcsJBadParameterEx();
cause.setParameter("curl");
cause.setParameterValue("null");
throw new AcsJContainerServicesEx(cause);
}
// check if our component has requested the other component before
org.omg.CORBA.Object stub = m_usedComponentsMap.get(curl);
if (stub != null)
{
// reusing seems ok as long as there is one separate
// instance of ContainerServicesImpl for each component.
// This reuse does not cut off the manager from component access,
// since it only happens at second or further access attempts;
// the first time, the component reference is obtained through the manager
// (independently of whether the components are collocated inside the same container),
// so that the manager is aware of component dependencies.
m_logger.info("client '" + m_clientName + "' attempts to retrieve component '" +
curl + "' more than once; will return existing reference.");
}
else
{
m_logger.fine("will retrieve remote component '" + curl +
"' using ACS Manager#get_component with client handle " + getEffectiveClientHandle());
/// @todo: think about timeouts
try {
stub = m_acsManagerProxy.get_component(getEffectiveClientHandle(), curl, true);
m_logger.fine("component " + curl + " retrieved successfully.");
m_usedComponentsMap.put(curl, stub);
} catch (AcsJmaciErrTypeEx ex) {
String msg = "Failed to retrieve component " + curl;
m_logger.log(Level.FINE, msg, ex); // only a low-level log because the client component is supposed to log the exception which contains all context data
throw new AcsJContainerServicesEx(ex);
} catch (Throwable thr) {
String msg = "Failed to retrieve component " + curl + " for unexpected reasons.";
m_logger.log(Level.FINE, msg, thr);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(thr);
ex.setContextInfo(msg);
throw ex;
}
}
return stub;
}
public org.omg.CORBA.Object getComponentNonSticky(String curl)
throws AcsJContainerServicesEx
{
if (curl == null) {
AcsJBadParameterEx cause = new AcsJBadParameterEx();
cause.setParameter("curl");
cause.setParameterValue("null");
throw new AcsJContainerServicesEx(cause);
}
org.omg.CORBA.Object stub = null;
try {
stub = m_acsManagerProxy.get_component_non_sticky(getEffectiveClientHandle(), curl);
m_logger.fine("Non-sticky reference to component '" + curl + "' retrieved successfully.");
m_usedNonStickyComponentsMap.put(curl, stub);
} catch (AcsJmaciErrTypeEx ex) {
String msg = "Failed to retrieve non-sticky reference to component " + curl;
m_logger.log(Level.FINE, msg, ex); // only a low-level log because the client component is supposed to log the exception which contains all context data
throw new AcsJContainerServicesEx(ex);
} catch (Throwable thr) {
String msg = "Failed to retrieve non-sticky reference to component '" + curl + "' for unexpected reasons.";
m_logger.log(Level.FINE, msg, thr);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(thr);
ex.setContextInfo(msg);
throw ex;
}
return stub;
}
/**
* @see alma.acs.container.ContainerServices#getDefaultComponent(java.lang.String)
*/
public org.omg.CORBA.Object getDefaultComponent(String componentIDLType)
throws AcsJContainerServicesEx
{
if (componentIDLType == null) {
AcsJBadParameterEx cause = new AcsJBadParameterEx();
cause.setParameter("componentIDLType");
cause.setParameterValue("null");
throw new AcsJContainerServicesEx(cause);
}
ComponentInfo cInfo = null;
try
{
// the call
cInfo = m_acsManagerProxy.get_default_component(getEffectiveClientHandle(), componentIDLType);
}
catch (AcsJmaciErrTypeEx ex) {
String msg = "failed to retrieve default component for type " + componentIDLType;
m_logger.log(Level.FINE, msg, ex); // higher-level log should be produced by the calling client from the exception later
throw new AcsJContainerServicesEx(ex);
}
catch (Throwable thr) {
String msg = "failed to retrieve default component for type " + componentIDLType + " for unexpected reasons!";
m_logger.log(Level.FINE, msg, thr);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(thr);
ex.setContextInfo(msg);
throw ex;
}
// cInfo.reference == null should no longer happen since the maci exception changes for ACS 6.0
// @todo check and remove this
if (cInfo.reference == null) {
String msg = "Default component for type '" + componentIDLType + "' could not be accessed. ";
m_logger.info(msg);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx();
ex.setContextInfo(msg);
throw ex;
}
m_usedComponentsMap.put(cInfo.name, cInfo.reference);
m_componentDescriptorMap.put(cInfo.name, new ComponentDescriptor(cInfo));
return cInfo.reference;
}
public org.omg.CORBA.Object getCollocatedComponent(String compUrl, String targetCompUrl) throws AcsJContainerServicesEx {
if (compUrl == null) {
AcsJBadParameterEx cause = new AcsJBadParameterEx();
cause.setParameter("compUrl");
cause.setParameterValue("null");
throw new AcsJContainerServicesEx(cause);
}
if (targetCompUrl == null) {
AcsJBadParameterEx cause = new AcsJBadParameterEx();
cause.setParameter("targetCompUrl");
cause.setParameterValue("null");
throw new AcsJContainerServicesEx(cause);
}
ComponentQueryDescriptor cqd = new ComponentQueryDescriptor(compUrl, null);
return getCollocatedComponent(cqd, false, targetCompUrl);
}
public org.omg.CORBA.Object getCollocatedComponent(ComponentQueryDescriptor spec, boolean markAsDefaul, String targetCompUrl) throws AcsJContainerServicesEx {
if (spec == null) {
AcsJBadParameterEx cause = new AcsJBadParameterEx();
cause.setParameter("ComponentQueryDescriptor");
cause.setParameterValue("null");
throw new AcsJContainerServicesEx(cause);
}
if (targetCompUrl == null) {
AcsJBadParameterEx cause = new AcsJBadParameterEx();
cause.setParameter("targetCompUrl");
cause.setParameterValue("null");
throw new AcsJContainerServicesEx(cause);
}
ComponentInfo cInfo = null;
try {
// the call
cInfo = m_acsManagerProxy.get_collocated_component(getEffectiveClientHandle(), spec.toComponentSpec(), false, targetCompUrl);
} catch (AcsJmaciErrTypeEx ex) {
String msg = "Failed to retrieve component '" + spec.getComponentName() + "' created such that it runs collocated with '"+ targetCompUrl + "'.";
m_logger.log(Level.FINE, msg, ex); // it's serious, but the caller is supposed to log this. Container only logs just in case.
throw new AcsJContainerServicesEx(ex);
}
catch (Throwable thr) {
String msg = "Unexpectedly failed to retrieve component '" + spec.getComponentName() + "' created such that it runs collocated with '"+ targetCompUrl + "'.";
m_logger.log(Level.FINE, msg, thr);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(thr);
ex.setContextInfo(msg);
throw ex;
}
// cInfo.reference == null should no longer happen since the maci exception changes for ACS 6.0
// @todo check and remove this
if (cInfo.reference == null) {
String msg = "Failed to retrieve component '" + spec.getComponentName() + "' created such that it runs collocated with '"+ targetCompUrl + "'.";
m_logger.info(msg);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx();
ex.setContextInfo(msg);
throw ex;
}
m_usedComponentsMap.put(cInfo.name, cInfo.reference);
m_componentDescriptorMap.put(cInfo.name, new ComponentDescriptor(cInfo));
return cInfo.reference;
}
/**
* @see alma.acs.container.ContainerServices#getDynamicComponent(si.ijs.maci.ComponentSpec, boolean)
*/
public org.omg.CORBA.Object getDynamicComponent(ComponentQueryDescriptor compDesc, boolean markAsDefault)
throws AcsJContainerServicesEx
{
return getDynamicComponent(compDesc.toComponentSpec(), markAsDefault);
}
/**
* @see alma.acs.container.ContainerServices#getDynamicComponent(si.ijs.maci.ComponentSpec, boolean)
*/
public org.omg.CORBA.Object getDynamicComponent(ComponentSpec compSpec, boolean markAsDefault)
throws AcsJContainerServicesEx
{
String entryMsg = "getDynamicComponent called with" +
" compName=" + compSpec.component_name +
" compType=" + compSpec.component_type +
" compCode=" + compSpec.component_code +
" compContainer=" + compSpec.container_name +
" markAsDefault=" + markAsDefault;
m_logger.fine(entryMsg);
ComponentInfo cInfo = null;
try
{
// the call
cInfo = m_acsManagerProxy.get_dynamic_component(getEffectiveClientHandle(), compSpec, markAsDefault);
m_usedComponentsMap.put(cInfo.name, cInfo.reference);
m_componentDescriptorMap.put(cInfo.name, new ComponentDescriptor(cInfo));
} catch (AcsJmaciErrTypeEx ex) {
m_logger.log(Level.FINE, "Failed to create dynamic component", ex);
throw new AcsJContainerServicesEx(ex);
}
catch (Throwable thr) {
String msg = "Unexpectedly failed to create dynamic component for unexpected reasons!";
m_logger.log(Level.FINE, msg, thr);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(thr);
ex.setContextInfo(msg);
throw ex;
}
return cInfo.reference;
}
public org.omg.CORBA.Object getReferenceWithCustomClientSideTimeout(org.omg.CORBA.Object originalCorbaRef, double timeoutSeconds)
throws AcsJContainerServicesEx {
return acsCorba.wrapForRoundtripTimeout(originalCorbaRef, timeoutSeconds);
}
/**
* @see alma.acs.container.ContainerServices#getCDB()
*/
@Override
public DAL getCDB() throws AcsJContainerServicesEx {
return cdb;
}
/**
* Releases the specified component reference. This involves notification of the manager,
* as well as calling <code>_release()</code> on the CORBA stub.
* If the curl is not known to the container, the request will be ignored.
* <p>
* Note that <i>references</i> to other components are released by this method,
* where the components hosted inside this container act as clients.
* These referenced components may run inside this or some other container/container.
* <p>
* Since ACS 9.1 this method is implemented by delegating to
* {@link #releaseComponent(String, alma.acs.container.ContainerServices.ComponentReleaseCallback)},
* keeping the old synchronous behavior by blocking on a callback object, with a timeout of 60 seconds.
* Since ACS 10.0, errors are logged at level DEBUG.
*
* @see alma.acs.container.ContainerServices#releaseComponent(java.lang.String)
*/
public void releaseComponent(String curl) {
ComponentReleaseCallback callback = new ComponentReleaseCallbackWithLogging(m_logger, AcsLogLevel.DEBUG);
releaseComponent(curl, callback);
try {
callback.awaitComponentRelease(60, TimeUnit.SECONDS);
} catch (InterruptedException ex) {
m_logger.log(AcsLogLevel.DEBUG, "Interrupted while waiting for release of component " + curl);
}
}
/**
* Used by {@link ContainerServicesImpl#releaseComponent(String, alma.acs.container.ContainerServices.ComponentReleaseCallback)}
* to wrap the user-supplied <code>ComponentReleaseCallback</code> for usage over Corba and for cleaner exception dispatching.
*/
private class ComponentReleaseCallbackCorbaHandler extends ResponseReceiver<Integer> {
private final ComponentReleaseCallback delegate;
private final org.omg.CORBA.Object stub;
/**
* @param delegate The user-provided callback pojo (unaware of corba)
* @param stub The client stub that will be released only when the component has been deactivated.
*/
ComponentReleaseCallbackCorbaHandler(ComponentReleaseCallback delegate, org.omg.CORBA.Object stub ) {
this.delegate = delegate;
this.stub = stub;
}
@Override
public void incomingException(AcsJException ex) {
try {
if (ex instanceof AcsJComponentDeactivationUncleanEx) {
delegate.componentReleased((AcsJComponentDeactivationUncleanEx)ex);
}
else if (ex instanceof AcsJComponentDeactivationFailedEx) {
delegate.errorComponentReleaseFailed((AcsJComponentDeactivationFailedEx)ex);
}
else if (ex instanceof AcsJUnexpectedExceptionEx) {
// This is thrown by jmanager :: ManagerProxyImpl#release_component_async if it gets a non-AcsJException
// from container#deactivate_component, e.g. org.omg.CORBA.COMM_FAILURE (leading to an internal com.cosylab.acs.maci.RemoteException)
delegate.errorCommunicationFailure(ex); // strictly speaking the wrong method, but better than nothing.
}
else {
m_logger.log(Level.WARNING, "Received unexpected exception from manager#release_component_async, please report to ACS developers.", ex);
delegate.errorCommunicationFailure(ex); // strictly speaking the wrong method, but better than nothing.
}
}
catch (RuntimeException handlerEx) {
m_logger.log(Level.FINE, "User-supplied handler threw an exception.", handlerEx);
}
finally {
delegate.callOver();
stub._release();
}
}
@Override
public void incomingResponse(Integer numberRemainingClients) {
// we do not expose numberRemainingClients in the CS API
try {
delegate.componentReleased(null);
}
catch (RuntimeException handlerEx) {
m_logger.log(Level.FINE, "User-supplied handler threw an exception.", handlerEx);
}
finally {
delegate.callOver();
stub._release();
}
}
}
@Override
public void releaseComponent(String curl, ComponentReleaseCallback callback) {
// we keep the "forceful" release option as a switch in the code.
// It was taken out for ACS 7.0, but may come back in the future.
final boolean forcibly = false;
if (curl == null) {
String msg = "Invalid curl 'null', nothing to release.";
m_logger.log(( callback == null ? AcsLogLevel.INFO : AcsLogLevel.DEBUG ), msg);
if (callback != null) {
callback.errorNoPermission(msg);
callback.callOver();
}
return;
}
org.omg.CORBA.Object stub = null;
// This use of synchronized makes the code thread safe without locking across the remote call to manager#release_component etc
synchronized (m_usedComponentsMap) {
if (!m_usedComponentsMap.containsKey(curl)) {
String msg = "ignoring request by client '" + m_clientName + (
m_usedNonStickyComponentsMap.containsKey(curl)
? "' to release component '" + curl + "' because the reference is non-sticky and does not need to be released."
: "' to release other component with unknown curl='" + curl + "'."
);
m_logger.log(( callback == null ? AcsLogLevel.INFO : AcsLogLevel.DEBUG ), msg);
if (callback != null) {
callback.errorNoPermission(msg);
callback.callOver();
}
return;
}
// the CURL is in the map and gets removed now
stub = m_usedComponentsMap.get(curl);
m_usedComponentsMap.remove(curl);
}
m_logger.fine("about to release component " + curl + (forcibly ? " forcibly" : ""));
try {
if (forcibly) {
m_acsManagerProxy.force_release_component(getEffectiveClientHandle(), curl);
}
else {
CBlong myCBlong = null;
if (callback != null) {
// @TODO reuse ComponentReleaseCallbackCorbaHandler
ComponentReleaseCallbackCorbaHandler callbackCorba = new ComponentReleaseCallbackCorbaHandler(callback, stub);
myCBlong = RequesterUtil.giveCBLong(this, callbackCorba);
}
m_acsManagerProxy.release_component(getEffectiveClientHandle(), curl, myCBlong);
}
m_logger.info("client '" + m_clientName + "' has successfully delivered a component release request for curl=" + curl);
if (callback == null) {
stub._release();
}
else {
// _release() is deferred until ComponentReleaseCallbackCorbaHandler gets the callback,
// to not abort running calls with COMM_FAILURE
}
}
catch (AcsJNoPermissionEx ex) {
AcsLogLevel level = ( callback == null ? AcsLogLevel.WARNING : AcsLogLevel.DEBUG );
m_logger.log(level, "client '" + m_clientName + "' (handle " + getEffectiveClientHandle() + ") cannot release " +
" with the manager the component with curl=" + curl, ex);
if (callback != null) {
callback.errorNoPermission(ex.getReason());
}
}
catch (Throwable thr) { // any org.omg.CORBA.SystemException, or whatever else can happen
AcsLogLevel level = ( callback == null ? AcsLogLevel.WARNING : AcsLogLevel.DEBUG );
m_logger.log(level, "client '" + m_clientName + "' (handle " + getEffectiveClientHandle() + ") failed to release " +
" with the manager the component with curl=" + curl, thr);
if (callback != null) {
callback.errorCommunicationFailure(thr);
}
}
}
/**
* @see alma.acs.container.ContainerServices#activateOffShoot(org.omg.PortableServer.Servant)
*/
@Override
public <T extends Servant & OffShootOperations> OffShoot activateOffShoot(T servant)
throws AcsJContainerServicesEx
{
return activateOffShoot(servant, null);
}
/**
* @see alma.acs.container.ContainerServices#activateOffShoot(org.omg.PortableServer.Servant)
*/
@Override
public <T extends OffShootOperations> OffShoot activateOffShoot(T offshootImpl, Class<T> idlOpInterface)
throws AcsJContainerServicesEx
{
Servant servant = null;
boolean isTie = false;
boolean haveToInject = false;
// Checks
checkOffShoot(offshootImpl);
// If we receive an object that is not a servant it means that it requires XML automatic bindings.
// We create the corresponding POATie object, the dynamic proxy binder,
// and set the offshoot implementation as the final delegate
if( !(offshootImpl instanceof Servant) ) {
if( idlOpInterface == null )
throw new AcsJContainerServicesEx(new NullPointerException("Received null idlOpInterface when asking to activate XML offshoot"));
if( !idlOpInterface.isAssignableFrom(offshootImpl.getClass()) ) {
AcsJContainerServicesEx ex = new AcsJContainerServicesEx();
ex.setContextInfo("Received OffShoot of type '" + offshootImpl.getClass().getName() +
"' does not inherits from '" + idlOpInterface.getName() + "'");
throw ex;
}
// Guess the name of the xyzPOATie class, build it, and delegate
String poaTieClassName = null;
try {
m_logger.fine("Creating POATie servant for offshoot '" + offshootImpl.getClass().getName() + "'");
// Get the POATie class and the expected xyzOperations interface
String baseClassName = idlOpInterface.getName().substring(0, idlOpInterface.getName().length()-1);
poaTieClassName = baseClassName + "POATie";
Class<?> poaTieClazz = Class.forName( poaTieClassName );
Method implGetter = poaTieClazz.getMethod("_delegate", (Class[]) null);
Class<?> operationsIF = implGetter.getReturnType();
// Create the dynamic XML entities wrapper
Object proxy = DynamicProxyFactory.getDynamicProxyFactory(m_logger)
.createServerProxy(operationsIF, offshootImpl, idlOpInterface);
// Create the POATie object, give it the proxy, and set it as our servant
Constructor<?> c = poaTieClazz.getConstructor(new Class[]{operationsIF});
servant = (Servant)c.newInstance(proxy);
if( m_componentXmlTranslatorProxy != null )
haveToInject = true;
} catch (ClassNotFoundException e) {
String msg = "Failed to create servant for offshoot " + offshootImpl.getClass().getName() + ": class '" + poaTieClassName + "' cannot be found";
m_logger.log(AcsLogLevel.ERROR, msg, e);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx();
ex.setContextInfo(msg);
throw ex;
} catch(Exception e) {
throw new AcsJContainerServicesEx(e);
}
}
else {
m_logger.fine("Don't need to create servant for offshoot '" + offshootImpl.getClass().getName() + "'");
servant = (Servant)offshootImpl;
}
// check if the servant is the Tie variant, which allows proxy-based call interception by the container
String servantName = servant.getClass().getName();
if (servantName.endsWith("POATie")) {
try {
// the _delegate getter method is mandated by the IDL-to-Java mapping spec
Method implGetter = servant.getClass().getMethod("_delegate", (Class[]) null);
isTie = true;
Class<?> operationsIF = implGetter.getReturnType();
java.lang.Object offshootTiedImpl = implGetter.invoke(servant, (java.lang.Object[]) null);
// now we insert the interceptor between the tie skeleton and the impl.
// Offshoots have no name, so we construct one from the component name and the offshoot interface name
//
String qualOffshootName = getName() + "/" + operationsIF.getName().substring(0, operationsIF.getName().length() - "Operations".length());
java.lang.Object interceptingOffshootImpl = ContainerSealant.createContainerSealant(
operationsIF, offshootTiedImpl, qualOffshootName, true, m_logger,
Thread.currentThread().getContextClassLoader(), methodsExcludedFromInvocationLogging);
Method implSetter = servant.getClass().getMethod("_delegate", new Class[]{operationsIF});
implSetter.invoke(servant, new java.lang.Object[]{interceptingOffshootImpl});
m_logger.fine("created sealant for offshoot " + qualOffshootName);
} catch (NoSuchMethodException e) {
// so this was not a Tie skeleton, even though its name ends misleadingly with "POATie"
} catch (Exception e) {
m_logger.log(Level.WARNING, "Failed to create interceptor for offshoot " + servantName, e);
}
}
if (!isTie) {
// TODO: perhaps require tie offshoots with ACS 5.0, and enable this warning log
// m_logger.warning("Offshoot servant '" + servantName + "' from component '" + getName() +
// "' does not follow the tie approach. Calls can thus not be intercepted by the container.");
}
OffShoot shoot = null;
try {
org.omg.CORBA.Object obj = acsCorba.activateOffShoot(servant, m_clientPOA);
m_activatedOffshootsMap.put(offshootImpl, servant);
shoot = OffShootHelper.narrow(obj);
}
catch (Throwable thr) {
String msg = "failed to activate offshoot object of type '" + servant.getClass().getName() +
"' for client '" + m_clientName + "'. ";
// flatten the exception chain by one level if possible
if (thr instanceof AcsJContainerServicesEx && thr.getCause() != null) {
msg += "(" + thr.getMessage() + ")";
thr = thr.getCause();
}
m_logger.log(Level.FINE, msg, thr);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(thr);
throw ex;
}
// finally, put the CORBA-object/implementation into the component's proxy invocation handler,
// so when requesting an offshoot into the component, we return the corresponding CORBA object
if( haveToInject ) {
m_logger.fine("Injecting offshoot '" + offshootImpl.getClass().getName() + "' to '" + m_clientName + "' component XML binder");
ComponentInvocationHandler handler = (ComponentInvocationHandler)Proxy.getInvocationHandler(m_componentXmlTranslatorProxy);
handler.addOffshoot(offshootImpl, shoot);
}
m_logger.fine("successfully activated offshoot of type " + offshootImpl.getClass().getName());
return shoot;
}
@Override
public void deactivateOffShoot(Object offshootImpl)
throws AcsJContainerServicesEx
{
checkOffShoot(offshootImpl);
try {
acsCorba.deactivateOffShoot(m_activatedOffshootsMap.get(offshootImpl), m_clientPOA);
m_activatedOffshootsMap.remove(offshootImpl);
m_logger.fine("successfully deactivated offshoot of type " + offshootImpl.getClass().getName());
} catch (AcsJContainerEx e) {
throw new AcsJContainerServicesEx(e);
}
}
/**
* @param cbServant
* @throws ContainerException
*/
private void checkOffShoot(Object servant) throws AcsJContainerServicesEx {
if (servant == null) {
AcsJBadParameterEx cause = new AcsJBadParameterEx();
cause.setParameter("servant");
cause.setParameterValue("null");
throw new AcsJContainerServicesEx(cause);
}
if (!(servant instanceof OffShootOperations)) {
String msg = "invalid offshoot servant provided. Must implement " + OffShootOperations.class.getName();
m_logger.fine(msg);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx();
ex.setContextInfo(msg);
throw ex;
}
}
/**
* @see alma.acs.container.ContainerServices#getAdvancedContainerServices()
*/
public synchronized AdvancedContainerServices getAdvancedContainerServices() {
synchronized (lazyCreationSync) {
if (advancedContainerServices == null) {
advancedContainerServices = new AdvancedContainerServicesImpl(this, m_logger);
// todo: once the legitimate cases of calling this method are settled, remove the log message.
m_logger.info("component '" + getName() + "' requested AdvancedContainerServices");
}
}
return advancedContainerServices;
}
/**
* {@inheritDoc}.
* <p>
* TODO: implement shortcutting of xml (de-)serialization for collocated component or offshoot:
* ask AcsContainer if it knows componentReference, and if it has transpXml-IF;
* if so, get component impl directly;
* check if respective component helper allows direct calls to transpXmlIF
* (by not implementing _getInterfaceTranslator, or some explicit flag);
* move intercepting layer (ContainerSealant) so that it's still in between the components.
*
* @see alma.acs.container.ContainerServices#getTransparentXmlComponent(java.lang.Class, org.omg.CORBA.Object, java.lang.Class)
*/
public <T, F> T getTransparentXmlWrapper(Class<T> transparentXmlIF, F flatXmlObject, Class<F> flatXmlIF)
throws AcsJContainerServicesEx
{
if (m_logger.isLoggable(Level.FINEST)) {
m_logger.finest("creating xml binding class aware wrapper around remote object " +
"implementing " + flatXmlIF.getName() + "...");
}
T wrapper = null;
try
{
wrapper = DynamicProxyFactory.getDynamicProxyFactory(m_logger).createClientProxy(
transparentXmlIF,
flatXmlObject,
flatXmlIF);
}
catch (Throwable thr)
{
String msg = "failed to create XML binding class wrapper for remote object implementing " + flatXmlIF.getName();
m_logger.log(Level.FINE, msg, thr);
AcsJContainerServicesEx ex2 = new AcsJContainerServicesEx(thr);
ex2.setContextInfo(msg);
throw ex2;
}
return wrapper;
}
/////////////////////////////////////////////////////////////
// other
/////////////////////////////////////////////////////////////
public void releaseAllComponents()
{
// copy curls first to avoid deleting from m_usedComponentsMap
// while iterating over it (fail-fast iterator throws ConcurrentModificationException).
// synchronized just in case...
List<String> curls = new ArrayList<String>();
synchronized (m_usedComponentsMap) {
curls.addAll(m_usedComponentsMap.keySet());
}
for (String curl : curls ) {
releaseComponent(curl);
}
}
/**
* Gets the handle to be used toward the manager, which is
* <ul>
* <li> The handle obtained from the manager at login for normal clients
* <li> The component handle assigned by the manager at component activation time,
* if this ContainerServices instance is used for a component
* </ul>
* We don't cache the handle from acsManagerProxy because it may change after a re-login,
* and then we get errors if the stale handle would be used.
* @return The correct handle to be used to identify this client to the manager.
*/
private int getEffectiveClientHandle() {
return (m_componentHandle > 0 ? m_componentHandle : m_acsManagerProxy.getManagerHandle());
}
/**
* @see alma.acs.container.ContainerServices#getThreadFactory()
*/
@Override
public ThreadFactory getThreadFactory() {
return m_threadFactory;
}
AcsCorba getAcsCorba() {
return acsCorba;
}
/**
* With this optional call, automatic invocation logging for certain offshoot methods can be disabled.
* @param methodsExcludedFromInvocationLogging
* @see ComponentHelper#getComponentMethodsExcludedFromInvocationLogging()
*/
void setMethodsExcludedFromInvocationLogging(String[] methodsExcludedFromInvocationLogging) {
this.methodsExcludedFromInvocationLogging = methodsExcludedFromInvocationLogging;
}
/**
* Cleans up all the resources that need to be closed, like closing opened notification channels
*
* @since ACS 8.1.0
*/
public void cleanUp() {
/* Cleanup through externally registered callbacks */
for (CleanUpCallback cleanUpCallback : cleanUpCallbacks) {
try {
cleanUpCallback.containerServicesCleanUp();
}
catch (Throwable thr) {
m_logger.log(Level.WARNING, "Failed to clean up registered client object", thr);
}
}
/* Disconnect NC subscribers */
for(String channel: m_subscribers.keySet()) {
AcsEventSubscriber<?> subscriber = m_subscribers.get(channel);
try {
subscriber.disconnect();
String tmp[] = channel.split("/");
m_logger.log(AcsLogLevel.NOTICE, "Automatically disconnected subscriber for NC '" + tmp[tmp.length - 1] + "'");
} catch (AcsJIllegalStateEventEx e) {
// Silently ignore this exception, as the subscriber was already disconnected. Well done, developers! :)
} catch (AcsJCouldntPerformActionEx ex) {
m_logger.log(Level.WARNING, "Failed to disconnect subscriber (which the user should have done before).", ex);
}
}
/* Disconnect NC publishers */
for(String channel: m_publishers.keySet()) {
AcsEventPublisher<?> publisher = m_publishers.get(channel);
try {
publisher.disconnect();
String tmp[] = channel.split("/");
m_logger.log(AcsLogLevel.NOTICE, "Automatically disconnected publisher for NC '" + tmp[tmp.length - 1] + "'");
} catch (AcsJIllegalStateEventEx e) {
// Silently ignore this exception, as the subscriber was already disconnected. Well done, developers! :)
}
}
}
/**
* A hack, see {@link ContainerServicesImpl#registerCleanUpCallback(CleanUpCallback)}.
* @deprecated Should be removed along with old NC Consumer.
*/
public static interface CleanUpCallback {
public void containerServicesCleanUp();
}
/**
* This is a hack: NC classes can register themselves to be notified,
* in order to release remote Corba resources (and prevent crashes of Notify Service...).
* Note that without this hack, the lifecycle of NC classes is only managed by the application code,
* which means that ACS could not enforce the clean up.
*
* @param cb
* @since ACS 8.1.0
* @deprecated Since ACS 10.2. Should be removed along with old NC Consumer.
*/
public void registerCleanUpCallback(ContainerServicesImpl.CleanUpCallback cb) {
cleanUpCallbacks.add(cb);
}
private NamingContext getNameService() throws AcsJContainerServicesEx {
NamingContext nameService = null;
try
{
org.omg.CORBA.Object nameServiceObj = m_acsManagerProxy.get_service("NameService", false);
nameService = NamingContextHelper.narrow(nameServiceObj);
} catch (AcsJmaciErrTypeEx ex) {
m_logger.log(Level.FINE, "Failed to get the reference to the NameService service", ex);
throw new AcsJContainerServicesEx(ex);
} catch (Throwable thr) {
String msg = "Unexpectedly failed to get the NameService reference!";
m_logger.log(Level.FINE, msg, thr);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(thr);
ex.setContextInfo(msg);
throw ex;
}
return nameService;
}
/**
* @see alma.acs.container.ContainerServices#createNotificationChannelSubscriber(String)
*/
@Override
public <T> AcsEventSubscriber<T> createNotificationChannelSubscriber(String channelName, Class<T> eventType) throws AcsJContainerServicesEx {
return createNotificationChannelSubscriber(channelName, null, eventType); //TODO (rtobar): Is this fine? I'm only 99% sure
}
/**
* @TODO: once we support notification over other frameworks, check that configuration
* and instantiate some class other than NCSubscriber.
* @see alma.acs.container.ContainerServices#createNotificationChannelSubscriber(String, String)
*/
@Override
public <T> AcsEventSubscriber<T> createNotificationChannelSubscriber(String channelName, String channelNotifyServiceDomainName, Class<T> eventType) throws AcsJContainerServicesEx {
AcsEventSubscriber<T> subscriber = null;
try {
Object[] args = new Object[]{
channelName,
channelNotifyServiceDomainName,
this,
getNameService(),
m_clientName,
eventType
};
@SuppressWarnings("unchecked")
Class<AcsEventSubscriber<T>> clazz = (Class<AcsEventSubscriber<T>>) Class.forName(CLASSNAME_NC_SUBSCRIBER);
Constructor<AcsEventSubscriber<T>> constructor = clazz.getConstructor(String.class, String.class, ContainerServicesBase.class, NamingContext.class, String.class, Class.class);
subscriber = constructor.newInstance(args);
} catch(ClassNotFoundException e) {
// TODO: maybe we could prevent future NCSubscriber creation tries, since the class isn't and will not be loaded
// The same applies for the next "catch" block
m_logger.log(AcsLogLevel.ERROR, "Cannot create NC subscriber because the 'NCSubscriber' class is not present in the classpath", e);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(e);
ex.setContextInfo("'" + CLASSNAME_NC_SUBSCRIBER + "' class not present in the classpath");
throw ex;
} catch(ClassCastException e) {
m_logger.log(AcsLogLevel.ERROR, "Cannot create NC subscriber because loaded class is not of type 'AcsEventSubscriber", e);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(e);
ex.setContextInfo("'" + CLASSNAME_NC_SUBSCRIBER + "' class does not extend 'AcsEventSubscriber'");
throw ex;
} catch(Throwable e) {
if (e instanceof InvocationTargetException) { // ctor ex
e = e.getCause();
}
m_logger.log(AcsLogLevel.ERROR, "Unexpected error while creating new AcsEventSubscriber object", e);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(e);
throw ex;
}
m_subscribers.put( (channelNotifyServiceDomainName == null ? "" : channelNotifyServiceDomainName) + "/" + channelName, subscriber);
return subscriber;
}
/**
* @see ContainerServices#createNotificationChannelPublisher(String)
*/
@Override
public <T> AcsEventPublisher<T> createNotificationChannelPublisher(String channelName, Class<T> eventType) throws AcsJContainerServicesEx {
return createNotificationChannelPublisher(channelName, null, eventType);
}
/**
* @TODO: once we support notification over other frameworks, check that configuration
* and instantiate some class other than NCPublisher.
*
* @see ContainerServices#createNotificationChannelPublisher(String, String)
*/
@Override
public <T> AcsEventPublisher<T> createNotificationChannelPublisher(String channelName, String channelNotifyServiceDomainName, Class<T> eventType) throws AcsJContainerServicesEx {
AcsEventPublisher<T> publisher = null;
try {
Object[] args = new Object[]{
channelName,
channelNotifyServiceDomainName,
this,
getNameService()
};
// // TODO: Can we do this without the direct cast? The usual "asSubclass" is not enough
// because we don't create a subclass of Class<T> but rather of Class<U<T>>.
// Also the getGenericInterfaces / ParameterizedType trick does not work because because we don't have a
// concrete parameterized type.
Class<AcsEventPublisher<T>> clazz = (Class<AcsEventPublisher<T>>) Class.forName(CLASSNAME_NC_PUBLISHER);
Constructor<? extends AcsEventPublisher<T>> constructor = clazz.getConstructor(String.class, String.class, ContainerServicesBase.class, NamingContext.class);
publisher = constructor.newInstance(args);
} catch(ClassNotFoundException e) {
// TODO: maybe we could prevent future NCPublisher creation tries, since the class isn't and will not be loaded
// The same applies for the next "catch" block
m_logger.log(AcsLogLevel.ERROR, "Cannot create NC publisher because the 'NCPublisher' class is not present in the classpath", e);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(e);
ex.setContextInfo("'" + CLASSNAME_NC_PUBLISHER + "' class not present in the classpath");
throw ex;
} catch(ClassCastException e) {
m_logger.log(AcsLogLevel.ERROR, "Cannot create NC publisher because loaded class '" + CLASSNAME_NC_PUBLISHER + "' is not of type 'AcsEventPublisher", e);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(e);
ex.setContextInfo("'" + CLASSNAME_NC_PUBLISHER + "' class does not extend 'AcsEventPublisher'");
throw ex;
} catch(Throwable e) {
if (e instanceof InvocationTargetException) { // ctor ex, seen for example in ICT-1508
e = e.getCause();
}
m_logger.log(AcsLogLevel.ERROR, "Unexpected error while creating new AcsEventPublisher object", e);
AcsJContainerServicesEx ex = new AcsJContainerServicesEx(e);
throw ex;
}
m_publishers.put( (channelNotifyServiceDomainName == null ? "" : channelNotifyServiceDomainName) + "/" + channelName, publisher);
// crude and arbitrary measure against misbehaving clients. In one case we've seen more than 2000 suppliers created by accident.
if (m_publishers.size() > 200) {
m_logger.warning("Component or client '" + m_clientName + "' has already created " + m_publishers.size() + " event publishers. Developers should check if this is really necessary.");
}
return publisher;
}
@Override
public AlarmSource getAlarmSource() {
return ACSAlarmSystemInterfaceFactory.getAlarmSource(this);
}
}