use of gov.sandia.NotifyMonitoringExt.EventChannel in project ACS by ACS-Community.
the class Helper method createNotificationChannel.
/**
* Tries to create a notification channel (using quality of service and administrative properties
* specified by configQofS() and configAdminProps() respectively).
* If this succeeds, then registers this channel with the naming service.
* <p>
* Should only be invoked when the channel that this supplier or consumer is attempting to connect to
* does not exist.
* However even with prior check for the existence of this channel, a race condition with other suppliers or consumers
* can lead to multiple attempts to create the same channel, which will result in <code>NameAlreadyUsed</code> exception.
* <p>
* Design note: Currently the TAO notification extensions are used to synch channel creation with other clients
* by supplying the channel name to the factory.
* If we want to use only standard NC factories then we'd have to implement our own locking mechanisms in all
* ACS consumer and supplier classes, see http://jira.alma.cl/browse/COMP-2808
*
* @return Reference to the newly created channel.
* @param channelKind
* Kind of the channel as registered with the CORBA naming service.
* @param notifyFactoryName
* Name of the notification service as registered with the CORBA naming service.
* @throws AcsJException
* Standard ACS Java exception.
* @throws NameAlreadyUsed thrown if the channel of this name already exists.
*/
protected EventChannel createNotificationChannel(String channelKind, String notifyFactoryName) throws AcsJException, NameAlreadyUsed {
LOG_NC_ChannelCreated_ATTEMPT.log(m_logger, channelName, notifyFactoryName);
// return value
EventChannel retValue = null;
// to be assigned by factory
channelId = -1;
StopWatch stopwatch = new StopWatch();
try {
initializeNotifyFactory(notifyFactoryName);
// create the channel
// here we use the channel properties taken directly from our channel properties helper object.
// presumably these values come from the ACS configuration database.
IntHolder channelIdHolder = new IntHolder();
retValue = createNotifyChannel_internal(m_channelProperties.configQofS(channelName), m_channelProperties.configAdminProps(channelName), channelIdHolder);
// sanity check
if (retValue == null) {
// a null reference implies we cannot go any further
Throwable cause = new Throwable("Null reference obtained for the '" + channelName + "' channel!");
// TODO: more specific ex type
throw new alma.ACSErrTypeJavaNative.wrappers.AcsJJavaLangEx(cause);
}
channelId = channelIdHolder.value;
// register our new channel with the naming service
try {
NameComponent[] t_NameChannel = { new NameComponent(combineChannelAndDomainName(channelName, domainName), channelKind) };
getNamingService().rebind(t_NameChannel, retValue);
// Create an entry into the Naming Service to store the timestamp of the channel in order to allow
// subscribers to reconnect to the channel (ICT-4730)
int maxNumAttempts = 10;
int nAttempts = maxNumAttempts;
boolean timestampCreated = setChannelTimestamp(retValue);
while (false == timestampCreated && nAttempts > 0) {
try {
Thread.sleep(2000);
} catch (InterruptedException ex1) {
// too bad
}
nAttempts--;
timestampCreated = setChannelTimestamp(retValue);
}
if (false == timestampCreated) {
Throwable cause = new Throwable("Failed to register the timestamp of the channel '" + channelName + "' into the Naming Service after " + String.valueOf(maxNumAttempts) + " attempts");
// TODO: more specific ex type
throw new alma.ACSErrTypeJavaNative.wrappers.AcsJJavaLangEx(cause);
}
} catch (org.omg.CosNaming.NamingContextPackage.NotFound ex) {
// Corba spec: "If already bound, the previous binding must be of type nobject;
// otherwise, a NotFound exception with a why reason of not_object is raised."
String reason = "Failed to register the new channel '" + channelName + "' with the Naming Service: " + ex.why.toString();
AcsJCORBAProblemEx ex2 = new AcsJCORBAProblemEx(ex);
ex2.setInfo(reason);
throw ex2;
}
} catch (org.omg.CosNaming.NamingContextPackage.CannotProceed e) {
// Think there is virtually no chance of this every happening but...
Throwable cause = new Throwable(e.getMessage());
throw new alma.ACSErrTypeCommon.wrappers.AcsJCORBAProblemEx(cause);
} catch (org.omg.CosNaming.NamingContextPackage.InvalidName e) {
// Think there is virtually no chance of this every happening but...
Throwable cause = new Throwable(e.getMessage());
throw new alma.ACSErrTypeCommon.wrappers.AcsJCORBAProblemEx(cause);
} catch (org.omg.CosNotification.UnsupportedQoS e) {
Throwable cause = new Throwable("The quality of service properties specified for the '" + channelName + "' channel are unsupported: " + e.getMessage());
throw new alma.ACSErrTypeCommon.wrappers.AcsJCORBAProblemEx(cause);
}
LOG_NC_ChannelCreated_OK.log(m_logger, channelName, channelId, notifyFactoryName, stopwatch.getLapTimeMillis());
return retValue;
}
use of gov.sandia.NotifyMonitoringExt.EventChannel in project ACS by ACS-Community.
the class HelperTest method testCreateChannel.
/**
* Creates and destroys a test channel.
* Also tests creating a second instance of that channel after the first one has been created,
* to check for the NameAlreadyUsed ex.
*/
public void testCreateChannel() throws Exception {
String channelName = "singleChannel";
Helper helper = new HelperWithChannelCreationSynch(channelName, getContainerServices(), nctx);
String factoryName = helper.getNotificationFactoryNameForChannel();
EventChannel myChannel = null;
try {
//precondition: channel not there (e.g. from previous run)
assertChannel(false, channelName);
// The call to "getNotificationChannel" should create the channel, because reuse will not be possible.
myChannel = helper.getNotificationChannel(factoryName);
assertChannel(true, channelName);
// Now we try to create that channel again, without allowing reuse. Should fail.
try {
helper.createNotificationChannel(NC_KIND.value, factoryName);
fail("Expected NameAlreadyUsed exception for creating the channel twice.");
} catch (Exception ex) {
m_logger.info("Got a NameAlreadyUsed exception as expected.");
}
// But with reuse it should work
EventChannel myChannel2 = helper.getNotificationChannel(factoryName);
assertTrue(myChannel._is_equivalent(myChannel2));
} finally {
// Destroy the channel
if (myChannel != null) {
helper.destroyNotificationChannel(NC_KIND.value, myChannel);
}
assertChannel(false, channelName);
}
}
use of gov.sandia.NotifyMonitoringExt.EventChannel in project ACS by ACS-Community.
the class HelperTest method testConcurrentChannelRetrieval.
/**
* One step up from {@link #testConcurrentChannelCreation()}, here we test concurrent calls to
* {@link Helper#getNotificationChannel(String, String, String)} which are supposed to handle the
* <code>NameAlreadyUsed</code> exception by making those later threads wait until the channel has
* been created for the first thread, then sharing the channel object.
*/
public void testConcurrentChannelRetrieval() throws Throwable {
// one channel to be retrieved concurrently
final String channelName = "testChannelForConcurrentRetrieval";
final HelperWithChannelCreationSynch helper = new HelperWithChannelCreationSynch(channelName, getContainerServices(), nctx);
assertChannel(false, channelName);
class ChannelRetriever implements Callable<EventChannel> {
private final CountDownLatch synchStart;
ChannelRetriever(CountDownLatch synchStart) {
this.synchStart = synchStart;
}
public EventChannel call() throws Exception {
String factoryName = helper.getNotificationFactoryNameForChannel();
return helper.getNotificationChannel(factoryName, synchStart);
}
}
// we need at least two threads, but more threads may improve collision chances
final int numCreators = 3;
assertTrue(numCreators >= 2);
ExecutorService pool = Executors.newFixedThreadPool(numCreators, getContainerServices().getThreadFactory());
CountDownLatch synchCreationStart = new CountDownLatch(numCreators);
List<Future<EventChannel>> results = new ArrayList<Future<EventChannel>>();
// check the results
EventChannel uniqueChannel = null;
try {
// Run the threads that request the same channel
for (int i = 0; i < numCreators; i++) {
results.add(pool.submit(new ChannelRetriever(synchCreationStart)));
}
// wait for all threads to finish. Waiting here instead of waiting on the future.get() calls
// has the advantage that we can exit this method with a fail() without leaving an ongoing channel creation behind.
pool.shutdown();
assertTrue(pool.awaitTermination(30, TimeUnit.SECONDS));
for (Future<EventChannel> future : results) {
try {
EventChannel threadResult = future.get();
// we only get here if threadResult != null, otherwise ex
if (uniqueChannel != null) {
assertTrue(uniqueChannel._is_equivalent(threadResult));
}
uniqueChannel = threadResult;
} catch (ExecutionException ex) {
throw ex.getCause();
} catch (AssertionFailedError ex) {
throw ex;
} catch (Throwable thr) {
fail("Unexpected exception " + thr.toString());
}
}
m_logger.info("All concurrent calls to getNotificationChannel got the same channel object.");
} finally {
if (uniqueChannel != null) {
helper.destroyNotificationChannel(NC_KIND.value, uniqueChannel);
}
}
}
use of gov.sandia.NotifyMonitoringExt.EventChannel in project ACS by ACS-Community.
the class HelperTest method testConcurrentChannelCreation.
/**
* Tests the collision case where many threads create the same channel concurrently.
* <p>
* Note that we would need to hack TAO to slow down (or otherwise synchronize with) channel creation,
* so that we can be sure that the second request comes in before the first request has finished.
* We optimize this by synchronizing the test threads right before they make the call to the channel factory,
* for which we overload the method {@link HelperWithChannelCreationSynch#createNotifyChannel_internal(EventChannelFactory, Property[], Property[], String, IntHolder)}.
* This eliminates jitter from thread creation, thread starting, and contact with the naming service, all happening before actual channel creation.
*/
public void testConcurrentChannelCreation() throws Exception {
// one channel tried to be created concurrently
final String channelName = "testChannelForConcurrentCreation";
final HelperWithChannelCreationSynch helper = new HelperWithChannelCreationSynch(channelName, getContainerServices(), nctx);
assertChannel(false, channelName);
// @TODO Refactor the following code to use alma.acs.concurrent.ThreadBurstExecutorService now that we have it
class ChannelCreator implements Callable<EventChannel> {
private final CountDownLatch synchStart;
ChannelCreator(CountDownLatch synchStart) {
this.synchStart = synchStart;
}
public EventChannel call() throws Exception {
String factoryName = helper.getNotificationFactoryNameForChannel();
return helper.createNotificationChannel(NC_KIND.value, factoryName, synchStart);
}
}
// we need at least two threads, but more threads may improve collision chances
final int numCreators = 4;
assertTrue(numCreators >= 2);
ExecutorService pool = Executors.newFixedThreadPool(numCreators, getContainerServices().getThreadFactory());
CountDownLatch synchCreationStart = new CountDownLatch(numCreators);
List<Future<EventChannel>> results = new ArrayList<Future<EventChannel>>();
// check the results
EventChannel uniqueChannel = null;
try {
// Run the threads that create the same channel
for (int i = 0; i < numCreators; i++) {
results.add(pool.submit(new ChannelCreator(synchCreationStart)));
}
// wait for all threads to finish. Waiting here instead of waiting on the future.get() calls
// has the advantage that we can exit this method with a fail() without leaving an ongoing channel creation behind.
pool.shutdown();
assertTrue(pool.awaitTermination(30, TimeUnit.SECONDS));
for (Future<EventChannel> future : results) {
try {
EventChannel threadResult = future.get();
// we only get here if threadResult != null, otherwise ex
if (uniqueChannel != null) {
fail("Only one thread should have managed to create the channel without exception!");
} else {
uniqueChannel = threadResult;
}
} catch (ExecutionException ex) {
if (ex.getCause() instanceof NameAlreadyUsed) {
m_logger.info("Got a NameAlreadyUsed exception");
} else {
fail("Unexpected exception " + ex.getCause().toString());
}
} catch (AssertionFailedError ex) {
throw ex;
} catch (Throwable thr) {
fail("Unexpected exception " + thr.toString());
}
}
assertNotNull("One thread should have succeeded", uniqueChannel);
} finally {
if (uniqueChannel != null) {
helper.destroyNotificationChannel(NC_KIND.value, uniqueChannel);
}
}
}
use of gov.sandia.NotifyMonitoringExt.EventChannel in project ACS by ACS-Community.
the class Helper method createNotifyChannel_internal.
/**
* Broken out from {@link #createNotificationChannel(String, String, String)}
* to give tests better control about the timing when this call to the event factory is made.
* @throws NameAlreadyUsed if the call to NotifyFactory#create_named_channel fails with this exception.
* @throws AcsJCORBAProblemEx if the TAO extension throws a NameMapError or if the QoS attributes cause a UnsupportedAdmin.
*/
protected EventChannel createNotifyChannel_internal(Property[] initial_qos, Property[] initial_admin, IntHolder channelIdHolder) throws NameAlreadyUsed, UnsupportedQoS, AcsJNarrowFailedEx, AcsJCORBAProblemEx {
EventChannel ret = null;
StopWatch stopwatch = new StopWatch();
try {
// The TAO extension of the notify factory that we use declares only the plain EventChannel type,
// even though it creates the TAO-extension subtype.
org.omg.CosNotifyChannelAdmin.EventChannel eventChannelBaseType = notifyFactory.create_named_channel(initial_qos, initial_admin, channelIdHolder, channelName);
LOG_NC_ChannelCreatedRaw_OK.log(m_logger, channelName, channelIdHolder.value, stopwatch.getLapTimeMillis());
// re-create the client side corba stub, to get the extension subtype
ret = gov.sandia.NotifyMonitoringExt.EventChannelHelper.narrow(eventChannelBaseType);
} catch (BAD_PARAM ex) {
LOG_NC_TaoExtensionsSubtypeMissing.log(m_logger, channelName, EventChannel.class.getName(), org.omg.CosNotifyChannelAdmin.EventChannelHelper.id());
AcsJNarrowFailedEx ex2 = new AcsJNarrowFailedEx(ex);
ex2.setNarrowType(EventChannelHelper.id());
throw ex2;
} catch (NameMapError ex) {
String msg = "Got a TAO extension-specific NameMapError exception that means the TAO NC extension is not usable. Bailing out since we need the extension.";
m_logger.log(AcsLogLevel.ERROR, msg, ex);
AcsJCORBAProblemEx ex2 = new AcsJCORBAProblemEx(ex);
ex2.setInfo(msg);
throw ex2;
} catch (UnsupportedAdmin ex) {
AcsJCORBAProblemEx ex2 = new AcsJCORBAProblemEx(ex);
ex2.setInfo(createUnsupportedAdminLogMessage(ex));
throw ex2;
}
return ret;
}
Aggregations