Read only archive ; use https://github.com/JacORB/JacORB/issues for new issues
Bug 430 - Recursive valuetype support broken in JacORB notification service
Summary: Recursive valuetype support broken in JacORB notification service
Status: RESOLVED FIXED
Alias: None
Product: JacORB
Classification: Unclassified
Component: ORB (show other bugs)
Version: 2.0 beta3
Hardware: PC Windows 2000
: P1 blocker
Assignee: Gerald Brose
URL:
Depends on:
Blocks:
 
Reported: 2003-12-17 20:58 CET by Dongjun Sun
Modified: 2005-10-27 23:32 CEST (History)
1 user (show)

See Also:


Attachments
created patch for suggested solution (876 bytes, patch)
2005-10-08 14:17 CEST, Alphonse Bendt
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Dongjun Sun 2003-12-17 20:58:03 CET
JacORB notifcation service fails when passing recursive valuetypes. Recursive 
valuetypes can be passed without noticeable problems between a regular ORB 
client and a server. However it fails when EventChannel, after receiving the 
recursive valuetype from the supplier, tries to deliver it to the consumser.
The problem seems to be associated with how CDRInputStream demarshals recursive 
valuetypes. It fails to set "actualTypecode" for the recursive members.

Recursive value type:

	    valuetype Node
	    {
		public string name;
		public Node next;

		void print();
	    };

Consumer (ValuePushConsumer):

import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;

public class ValuePushConsumer {


  public static void main(String [] args)
  {
   try {
    org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);

    // name service
    NamingContextExt nc =
		NamingContextExtHelper.narrow(orb.resolve_initial_references
("NameService"));

    // get service reference
    org.omg.CORBA.Object service
        = orb.resolve_initial_references("NotificationService");

    // it is actually the factory
    org.omg.CosNotifyChannelAdmin.EventChannelFactory factory
        = org.omg.CosNotifyChannelAdmin.EventChannelFactoryHelper.narrow
(service);

    // list all channels from the factory
    org.omg.CosNotifyChannelAdmin.EventChannel channel = null;
    int[] ids = factory.get_all_channels();
    if(ids.length == 0) {
	// no channel exists.
	org.omg.CORBA.IntHolder idHolder =
	    new org.omg.CORBA.IntHolder();

	org.omg.CosNotification.Property[] qos =
	    new org.omg.CosNotification.Property[0];
	org.omg.CosNotification.Property[] adm =
	    new org.omg.CosNotification.Property[0];

	channel =  factory.create_channel(qos, adm, idHolder);
	nc.rebind( nc.to_name("valuetype_event.channel"), channel );

	System.out.println("Channel " + idHolder.value +
		   " created and bound to name valuetype_event.channel.");
    }
    else
    {
	// get the first channel
	channel = factory.get_event_channel(ids[0]);
    }


    // get its default consumer admin
    org.omg.CosNotifyChannelAdmin.ConsumerAdmin admin
        = channel.default_consumer_admin();

    // create a proxy
    org.omg.CORBA.IntHolder proxy_id = new org.omg.CORBA.IntHolder();
    org.omg.CosNotifyChannelAdmin.ProxySupplier proxy
        = admin.obtain_notification_push_supplier(
            org.omg.CosNotifyChannelAdmin.ClientType.ANY_EVENT, proxy_id);

    org.omg.CosNotifyChannelAdmin.ProxyPushSupplier supplier
        = org.omg.CosNotifyChannelAdmin.ProxyPushSupplierHelper.narrow(proxy);

    // allocate the consumer implemenation
    ValuePushConsumerImpl servant = new ValuePushConsumerImpl();

    // activate it on root poa
    org.omg.CORBA.Object obj = orb.resolve_initial_references("RootPOA");
    org.omg.PortableServer.POA rootPOA = org.omg.PortableServer.POAHelper.narrow
(obj);
    rootPOA.activate_object(servant);

    // activate the root poa
    org.omg.PortableServer.POAManager poa_manager = rootPOA.the_POAManager();
    poa_manager.activate();

    // get consumer object reference
    org.omg.CORBA.Object ref = rootPOA.servant_to_reference(servant);
    org.omg.CosNotifyComm.PushConsumer consumer =
                org.omg.CosNotifyComm.PushConsumerHelper.narrow(ref);

    // connect the consumer to the supplier proxy
    supplier.connect_any_push_consumer(consumer);

    System.out.println( "any push consumer is ready");

    // work loop
    orb.run();
   } catch(Exception e) {
    System.out.println( "caught exception:" + e);
   }
  }
}

Consumer: (ValuePushConsumerImpl)

class ValuePushConsumerImpl extends org.omg.CosNotifyComm.PushConsumerPOA
{
    public final void push(org.omg.CORBA.Any event)
    {
	System.out.println( "push consumer received a link node: ");

        Node node = (Node)event.extract_Value();
        node.print();
    }

    public final void disconnect_push_consumer()
    {
	System.out.println( "disconnected by channel");
    }

    public final void offer_change(org.omg.CosNotification.EventType[] added,
              org.omg.CosNotification.EventType[] removed) {}
}

Supplier:

public class ValuePushSupplier {
public static void main(String[] args)
{
   try {
    org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);

    // get service reference
    org.omg.CORBA.Object service
        = orb.resolve_initial_references("NotificationService");

    // it is actually the factory
    org.omg.CosNotifyChannelAdmin.EventChannelFactory factory
        = org.omg.CosNotifyChannelAdmin.EventChannelFactoryHelper.narrow
(service);

    // list all channels from the factory
    int[] ids = factory.get_all_channels();
    if(ids.length == 0) {
        // no channel exists.
        System.out.println( "pls create a channel");
        return;
    }

    // get the first channel
    org.omg.CosNotifyChannelAdmin.EventChannel channel
        = factory.get_event_channel(ids[0]);

    // get its default supplier admin
    org.omg.CosNotifyChannelAdmin.SupplierAdmin admin
        = channel.default_supplier_admin();

    // create a proxy
    org.omg.CORBA.IntHolder proxy_id = new org.omg.CORBA.IntHolder();
    org.omg.CosNotifyChannelAdmin.ProxyConsumer proxy
        = admin.obtain_notification_push_consumer(
            org.omg.CosNotifyChannelAdmin.ClientType.ANY_EVENT, proxy_id);

    org.omg.CosNotifyChannelAdmin.ProxyPushConsumer consumer
        = org.omg.CosNotifyChannelAdmin.ProxyPushConsumerHelper.narrow(proxy);

    // connect to it (optional)
    consumer.connect_any_push_supplier(null);

    // push events
    org.omg.CORBA.Any event = orb.create_any();

    System.out.println( "push 1 any events with value type");

    Node A = new NodeImpl("A");
    Node B = new NodeImpl("B");
    Node C = new NodeImpl("C");
    Node D = new NodeImpl("D");

    A.next = B;
    B.next = C;
    C.next = D;
    D.next = A;


    Node node = A;

    for(int i=0; i<1; i++) {
        event.insert_Value(node);
	consumer.push(event);
        node = node.next;
    }

    // disconnect from the channel
    consumer.disconnect_push_consumer();
   }
   catch(Exception ex) {
       ex.printStackTrace();
   }
}
}

The following runtime exceptions can be seen inside EventChannel:

DEBUG   [n.engine.TaskConfigurator] (): Entering Exceptionhandler for 
Task:org.jacorb.notification.engine.PushToConsumer
Task
org.omg.CORBA.BAD_INV_ORDER
        at org.jacorb.orb.TypeCode.checkActualTC(Unknown Source)
        at org.jacorb.orb.TypeCode.kind(Unknown Source)
        at org.jacorb.orb.CDROutputStream.write_TypeCode
(CDROutputStream.java:1379)
        at org.jacorb.orb.CDROutputStream.write_TypeCode
(CDROutputStream.java:1587)
        at org.jacorb.orb.CDROutputStream.write_TypeCode
(CDROutputStream.java:1579)
        at org.jacorb.orb.CDROutputStream.write_TypeCode
(CDROutputStream.java:1351)
        at org.jacorb.orb.CDROutputStream.write_any(CDROutputStream.java:701)
        at org.omg.CosEventComm._PushConsumerStub.push(Unknown Source)
        at org.jacorb.notification.ProxyPushSupplierImpl.deliverEvent
(ProxyPushSupplierImpl.java:126)
        at org.jacorb.notification.engine.PushToConsumerTask.doWork(Unknown 
Source)
        at org.jacorb.notification.engine.AbstractTask.run(Unknown Source)
        at EDU.oswego.cs.dl.util.concurrent.PooledExecutor$Worker.run
(PooledExecutor.java)
        at java.lang.Thread.run(Thread.java:534)

ERROR   [n.engine.TaskConfigurator] (): a disabled EventConsumer should not 
throw  an exception during deliverEvent
java.lang.NullPointerException
        at org.jacorb.notification.queue.BoundedPriorityEventQueue.addElement
(Unknown Source)
        at org.jacorb.notification.queue.AbstractBoundedEventQueue.put(Unknown 
Source)
        at org.jacorb.notification.ProxyPushSupplierImpl.deliverEvent
(ProxyPushSupplierImpl.java:130)
        at 
org.jacorb.notification.engine.TaskConfigurator.onPushToConsumerTaskError
(Unknown Source)
        at org.jacorb.notification.engine.TaskConfigurator$3.handleTaskError
(Unknown Source)
        at org.jacorb.notification.engine.AbstractTask.run(Unknown Source)
        at EDU.oswego.cs.dl.util.concurrent.PooledExecutor$Worker.run
(PooledExecutor.java)
        at java.lang.Thread.run(Thread.java:534)

When running under JBuilder debugger, it can be seen that "actualTypecode" for 
the Node recursive member "next" is not set, which causes the above exceptions. 
However the NodeImpl object itself has been correctly received by the 
EventChannel.

The following hacks have been applied to org.jacorb.orb.TypeCode,

 /**
     * Constructor for tk_value
     */

    public TypeCode (String id,
                     String _name,
                     short type_modifier,
                     org.omg.CORBA.TypeCode concrete_base,
                     org.omg.CORBA.ValueMember[] members)
    {
        kind = TCKind._tk_value;
        this.id = id;
        if (_name != null)
	{public TypeCode (String id,
		     String _name,
		     short type_modifier,
		     org.omg.CORBA.TypeCode concrete_base,
		     org.omg.CORBA.ValueMember[] members)
    {
	kind = TCKind._tk_value;
	this.id = id;
	if (_name != null)
	{
	    name = _name.replace('.','_'); // for orbixWeb Interop
	}
	else
	{
	    name = "";
        }
            name = _name.replace('.','_'); // for orbixWeb Interop
        }
        else
        {
            name = "";
        }
        value_modifier = type_modifier;
        content_type = concrete_base;
        setValueMembers(members);
	resolveRecursion(this);
    }

   /*
    * Resolve any recursive TypeCodes contained within this TypeCode.
    * @param actual The actual (non-recursive) TypeCode that replaces any
    * recursive TypeCodes contained in the actual TypeCode that have the same
    * RepositoryId
    */
   private void resolveRecursion (TypeCode actual)
   {
      if (member_type == null)
      {
         return;
      }

      org.omg.CORBA.TypeCode typeCode;
      TypeCode tc;
      for (int i = 0; i < member_type.length; i++)
      {
         typeCode = originalType (member_type[i]);
         if (typeCode instanceof TypeCode)
         {
            tc = (TypeCode)typeCode;

            switch (tc.kind)
            {
            case   TCKind._tk_struct:
            case   TCKind._tk_union:
               tc.resolveRecursion (actual);
               break;
            case   TCKind._tk_sequence:
               typeCode = originalType (tc.content_type);
               if (typeCode instanceof TypeCode)
               {
                  tc = (TypeCode)typeCode;

                  if (tc.is_recursive () && tc.id.equals (actual.id))
                  {
                     tc.setActualTC (actual);
                  }
                  else
                  {
                     tc.resolveRecursion (actual);
                  }
               }
               break;
	    case   0xffffffff:
		if (tc.id.equals (actual.id))
		{
		   tc.setActualTC (actual);
		}
		break;
            }
         }
      }
   }

This solves the problem. But apparently the hacks do not look fit as the 
orginal code only mentions struct and union, giving no intention to support 
recursion for values types. Actually JDK1.4.2 API doc denounces the example 
valuetype defintion as illegal. Yet Jacorb gives a similar definition in 
demo\value\idl.

I don't know enough of CORBA spec on valuetype support. But it looks to me 
TypeCode constructor for valuetypes apparently ignores recursion, which causes 
the problem described here.

It is crucial for us to have Notification service to be able to pass recursive 
valuetypes ( another ORB, visibroker, works fine in this regrad). Thanks for 
looking into this.
Comment 1 Alphonse Bendt 2004-03-03 14:29:28 CET
i have added a test to reveal the behaviour
see test test_pass_list_in_any in
test/regression/src/org/jacorb/test/notification/orb/value/ValueTest
(you have to comment in a line in the suite method to activate the test)
Comment 2 Alphonse Bendt 2004-05-09 03:09:07 CEST
sorry! the testcase is located in
test/regression/src/org/jacorb/test/orb/value/ValueTest.java

Comment 3 Alphonse Bendt 2005-10-08 14:17:40 CEST
Created attachment 187 [details]
created patch for suggested solution
Comment 4 Alphonse Bendt 2005-10-27 23:32:30 CEST
applied patch