/*
* This file is protected by Copyright. Please refer to the COPYRIGHT file
* distributed with this source distribution.
*
* This file is part of REDHAWK core.
*
* REDHAWK core 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 3 of the License, or (at your
* option) any later version.
*
* REDHAWK core 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 program. If not, see http://www.gnu.org/licenses/.
*/
package org.ossie.events;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.omg.CORBA.Any;
import org.omg.CosEventChannelAdmin.*;
import org.omg.CosEventComm.PushSupplier;
import org.omg.PortableServer.POAPackage.ObjectNotActive;
import org.omg.PortableServer.POAPackage.ServantAlreadyActive;
import org.omg.PortableServer.POAPackage.ServantNotActive;
import org.omg.PortableServer.POAPackage.WrongPolicy;
import org.apache.log4j.Logger;
import org.ossie.component.PortBase;
import org.ossie.properties.StructDef;
/**
* A port that can receive REDHAWK messages via point-to-point connections or
* distributed over event channels.
*/
@SuppressWarnings("deprecation")
public class MessageConsumerPort extends ExtendedEvent.MessageEventPOA implements PortBase {
public Object updatingPortsLock = new Object();
protected HashMap<String, EventCallback> callbacks = new HashMap<String, EventCallback>();
protected boolean active = false;
protected String name;
/**
* Internal class to adapt ProxyPushConsumer CORBA interface on a
* per-supplier basis.
*/
private class ProxyMessageConsumer extends ProxyPushConsumerPOA {
public ProxyMessageConsumer()
{
}
public ProxyMessageConsumer(String connectionId)
{
this.connectionId = connectionId;
}
public void push(final org.omg.CORBA.Any data)
{
MessageConsumerPort.this.messagesReceived(data);
}
public void connect_push_supplier(PushSupplier supplier)
{
this.supplier = supplier;
}
public void disconnect_push_consumer()
{
MessageConsumerPort.this.removeConsumer(this);
}
public void disconnect()
{
if (this.supplier != null) {
this.supplier.disconnect_push_supplier();
}
}
public String getConnectionId()
{
return this.connectionId;
}
private PushSupplier supplier = null;
private String connectionId = null;
}
/**
* Internal class to adapt SupplierAdmin CORBA interface and dispatch to
* MessageSupplierPort.
*/
private class SupplierAdminProxy extends SupplierAdminPOA {
public ProxyPushConsumer obtain_push_consumer() {
return MessageConsumerPort.this.createConsumer();
}
public ProxyPullConsumer obtain_pull_consumer() {
// Not implemented: only push consumer/supplier
throw new org.omg.CORBA.NO_IMPLEMENT("Message consumer port only supports push suppliers");
}
}
private List<ProxyMessageConsumer> consumers = new LinkedList<ProxyMessageConsumer>();
private SupplierAdminProxy supplierAdmin = null;
private Logger logger = Logger.getLogger(MessageConsumerPort.class.getName());
/**
* Create a new message consumer port with the default class logger.
*/
public MessageConsumerPort(String portName)
{
this.name = portName;
}
/**
* Create a new message consumer port, with a user-supplied logger.
*/
public MessageConsumerPort(String portName, Logger logger)
{
this(portName);
this.logger = logger;
}
public boolean isActive() {
return this.active;
}
public void setActive(final boolean active) {
this.active = active;
}
public String getName() {
return this.name;
}
public void setLogger(Logger logger)
{
this.logger = logger;
}
public String getRepid()
{
return ExtendedEvent.MessageEventHelper.id();
}
public String getDirection()
{
return CF.PortSet.DIRECTION_BIDIR;
}
/**
* Register a listener for a message.
*
* @param messageId the identifier of the message
* @param clazz the class object for the message payload
* @param listener the listener to be called when a message is received
* @param <E> the type of the message
*
* @since 2.0
*/
public <E extends StructDef> void registerMessage(String messageId, Class<E> clazz, MessageListener<E> listener)
{
synchronized (this.callbacks) {
this.callbacks.put(messageId, new MessageAdapter<E>(clazz, listener));
}
}
public void connectPort(final org.omg.CORBA.Object connection, final String connectionId) throws CF.PortPackage.InvalidPort, CF.PortPackage.OccupiedPort {
// Give a specific exception message for nil
if (connection == null) {
throw new CF.PortPackage.InvalidPort((short)1, "Nil object reference");
}
// Attempt to narrow the reference to an event channel; note this does
// not require the lock
EventChannel channel = null;
try {
channel = EventChannelHelper.narrow(connection);
} catch (final org.omg.CORBA.BAD_PARAM ex) {
// In this context, a CORBA.BAD_PARAM exception indicates that the
// object is of the wrong type
throw new CF.PortPackage.InvalidPort((short)1, "Object is not an event channel");
} catch (final org.omg.CORBA.SystemException ex) {
// If the object is not obviously the desired type, narrow will
// invoke _is_a, which may throw a CORBA exception if a remote
// object is unreachable (e.g., dead)
throw new CF.PortPackage.InvalidPort((short)1, "Object unreachable");
}
// Try to get a proxy supplier, which should only fail if there is some
// sort of communication issue
ProxyPushSupplier proxy_supplier = null;
try {
final ConsumerAdmin consumer_admin = channel.for_consumers();
proxy_supplier = consumer_admin.obtain_push_supplier();
} catch (final Exception ex) {
throw new CF.PortPackage.InvalidPort((short)1, "Event channel unreachable");
}
// Create a proxy consumer to handle messages from this event channel,
// and connect it with the proxy supplier
ProxyMessageConsumer consumer = new ProxyMessageConsumer(connectionId);
try {
org.omg.CORBA.Object consumer_obj = this.activateChild(consumer);
ProxyPushConsumer consumer_ref = ProxyPushConsumerHelper.narrow(consumer_obj);
proxy_supplier.connect_push_consumer(consumer_ref);
} catch (final Exception ex) {
throw new CF.PortPackage.InvalidPort((short)1, "Unable to create an event consumer");
}
consumer.connect_push_supplier(proxy_supplier);
synchronized (this.updatingPortsLock) {
this.consumers.add(consumer);
this.active = true;
}
}
public void disconnectPort(final String connectionId) {
ProxyMessageConsumer consumer = null;
synchronized (this.updatingPortsLock) {
consumer = this.findConsumerByConnectionId(connectionId);
if (consumer == null) {
return;
}
this.consumers.remove(consumer);
}
consumer.disconnect();
this.deactivateChild(consumer);
}
public org.omg.CosEventChannelAdmin.SupplierAdmin for_suppliers()
{
synchronized(this.updatingPortsLock) {
if (this.supplierAdmin == null) {
SupplierAdminProxy supplier_admin = new SupplierAdminProxy();
this.activateChild(supplier_admin);
this.supplierAdmin = supplier_admin;
}
}
// Java's CORBA implementation has a broken _this(); work around the
// problem by going directly to the POA to create a reference, then
// narrow the result
org.omg.CORBA.Object object = null;
try {
object = this.supplierAdmin._default_POA().servant_to_reference(this.supplierAdmin);
} catch (final ServantNotActive sna) {
throw new AssertionError("Servant not active");
} catch (final WrongPolicy wp) {
throw new AssertionError("Wrong POA policy");
}
return SupplierAdminHelper.narrow(object);
}
public org.omg.CosEventChannelAdmin.ConsumerAdmin for_consumers()
{
// Not implemented: only remote suppliers are supported
throw new org.omg.CORBA.NO_IMPLEMENT("Message consumer port only supports suppliers");
}
public void destroy()
{
// Not implemented: cannot be destroyed
throw new org.omg.CORBA.NO_IMPLEMENT("Message consumer port cannot be destroyed");
}
private ProxyMessageConsumer findConsumerByConnectionId(final String connectionId)
{
for (ProxyMessageConsumer consumer : this.consumers) {
if (connectionId.equals(consumer.getConnectionId())) {
return consumer;
}
}
return null;
}
private ProxyPushConsumer createConsumer()
{
ProxyMessageConsumer consumer = new ProxyMessageConsumer();
org.omg.CORBA.Object proxy = this.activateChild(consumer);
synchronized (this.updatingPortsLock) {
this.consumers.add(consumer);
}
return ProxyPushConsumerHelper.narrow(proxy);
}
private org.omg.CORBA.Object activateChild(org.omg.PortableServer.Servant servant)
{
try {
byte[] oid = this._poa().activate_object(servant);
return this._poa().id_to_reference(oid);
} catch (final ServantAlreadyActive exc) {
throw new AssertionError("Servant already active");
} catch (final ObjectNotActive exc) {
throw new AssertionError("Object not active");
} catch (final WrongPolicy exc) {
throw new AssertionError("Wrong policy");
}
}
private void deactivateChild(org.omg.PortableServer.Servant servant)
{
try {
org.omg.PortableServer.POA poa = servant._default_POA();
byte[] oid = poa.servant_to_id(servant);
poa.deactivate_object(oid);
} catch (final ServantNotActive exc) {
throw new AssertionError("Servant not active");
} catch (final ObjectNotActive exc) {
throw new AssertionError("Object not active");
} catch (final WrongPolicy exc) {
throw new AssertionError("Wrong policy");
}
}
private void removeConsumer(ProxyMessageConsumer consumer)
{
synchronized (this.updatingPortsLock) {
this.consumers.remove(consumer);
}
this.deactivateChild(consumer);
}
private void messagesReceived(final org.omg.CORBA.Any data)
{
// Ignore all payloads except CF.Properties
if (!data.type().equivalent(CF.PropertiesHelper.type())) {
return;
}
for (final CF.DataType message : CF.PropertiesHelper.extract(data)) {
this.dispatchMessage(message.id, message.value);
}
}
private void dispatchMessage(final String messageId, final org.omg.CORBA.Any messageData)
{
EventCallback message_callback = null;
synchronized (this.callbacks) {
message_callback = this.callbacks.get(messageId);
// If no callback is registered, present a meaningful warning
if (message_callback == null) {
String warning = "No callbacks registered for messages with id '"+messageId+"'.";
if (this.callbacks.isEmpty()) {
warning += " No callbacks are registered";
} else if (this.callbacks.size() == 1) {
warning += " The only registered callback is for message with id: "+this.callbacks.keySet().iterator().next();
} else {
warning += " The available message callbacks are for messages with any of the following id:";
for (final String available : this.callbacks.keySet()) {
warning += " " + available;
}
}
this.logger.warn(warning);
return;
}
}
message_callback.message(messageId, messageData);
}
/**
* @deprecated As of REDHAWK 2.0, replaced by {@link #registerMessage(String,Class,MessageListener)}
*/
@Deprecated
public void registerMessage(final String message_id, EventCallback _callback) {
this.callbacks.put(message_id, _callback);
}
@Deprecated
public void push(final Any data)
{
// This method was non-functional in prior releases
}
@Deprecated
public HashMap<String, ExtendedEvent.MessageEventPOA> getPorts() {
return new HashMap<String, ExtendedEvent.MessageEventPOA>();
}
@Deprecated
public Consumer_i local_consumer = null;
@Deprecated
public org.omg.CosEventChannelAdmin.ProxyPushConsumer local_consumer_ref = null;
@Deprecated
public SupplierAdmin_i supplier_admin = null;
@Deprecated
public org.omg.CosEventChannelAdmin.SupplierAdmin supplier_admin_ref = null;
@Deprecated
public EventCallback callback;
@Deprecated
public Class< ? > parent;
@Deprecated
public StructDef data_structure;
}