javablockingjxta

Java Vector<E> getting blocked with no apparent reason


I'm probably doing something wrong, but this is what happens:

I register a listener, wait for it to be called and when it executes and calls "Vector.clear()" it locks. The problem happens in the "pipeMsgEvent" method, here it is the, not so friendly-looking, code (if you want, go directly to "this is important" lines):

public class JxtaCustomer implements PipeMsgListener {
    public static final int ANSWER_OK        = 0;
    public static final int ANSWER_TIMEOUT   =-1;
    public static final int ANSWER_NOT_ASKED =-2;
    public static final int ANSWER_WORKING   =-3;

    public static final int REQ_SENT         = 0;
    public static final int REQ_INV_PIPE_ID  =-1;
    public static final int REQ_INV_PIPE_IO  =-2;
    public static final int REQ_INV_GROUP_ID =-3;

    protected int answer;

    protected JxtaGenericAdvertisement serviceAdv = null;
    protected JxtaNode ThePeer = null;

    JxtaBiDiPipe myBiDiPipe = null;
    protected Vector<Entry> output = null;

    public JxtaCustomer(JxtaGenericAdvertisement adv, JxtaNode peer)
    {
        ThePeer = peer;
        serviceAdv = new JxtaGenericAdvertisement((Element)adv.getDocument(new MimeMediaType("text/xml")));

        answer = ANSWER_NOT_ASKED;
        Vector<Entry> output = new Vector<Entry>();
    }

    public void pipeMsgEvent(PipeMsgEvent event)
    {
        System.out.println("It Works! Uhuuu");
        // We received a message
        Message message = event.getMessage();

        System.out.println("Lets GO! Uhuuu");
        Message.ElementIterator elIt = message.getMessageElementsOfNamespace( serviceAdv.getName() );

        System.out.println("A little bit more");
        answer = ANSWER_WORKING;

        System.out.println("enter");

        // This is important: Here I get stuck, "clear"
        // never appears on the screen
        output.clear();
        System.out.println("clear");
        while (elIt.hasNext()) {
            MessageElement mElem = elIt.next();
            System.out.println(mElem.getElementName() + " " + mElem.toString());
            output.add( new Entry( mElem.getElementName(), mElem.toString() ) );
        }
        System.out.println("leave");

        answer = ANSWER_OK;
    }

    public int sendRequest(Vector<Entry> params)
    {
        try {

            // Creating Pipe Advertisement
            String pipeAdvType = PipeAdvertisement.getAdvertisementType();
            PipeAdvertisement pipeAd = (PipeAdvertisement)AdvertisementFactory.newAdvertisement(pipeAdvType);

            URI pipeURI = new URI(serviceAdv.getPipeID());
            pipeAd.setPipeID( IDFactory.fromURI(pipeURI) );
            pipeAd.setType(PipeService.UnicastType);
            pipeAd.setName(serviceAdv.getName() + " Service Pipe");
            pipeAd.setDescription("Created by " + serviceAdv.getName());

            // Creating Group
            JxtaGroup jxgp = ThePeer.getGroupFromID( serviceAdv.getGroupID() );
            if (null == jxgp)
                return REQ_INV_GROUP_ID;

            PeerGroup group = jxgp.getPeerGroup();
            if (null == group)
                return REQ_INV_GROUP_ID;

            // This is important: In the JxtaBiDiPipe call I register
            // the callback, while I have access to the code, I think
            // the problem is in my code
            myBiDiPipe = new JxtaBiDiPipe( group, pipeAd, 30000, this);

            if (myBiDiPipe.isBound()) {
                Thread.sleep(500);

                Message myMessage = new Message();

                if (0 == params.size())
                    params.add( new Entry("dummy", "null") );

                for (int i=0; i<params.size(); i++) {
                    String Key = params.get(i).getKey();
                    String Value = params.get(i).getValue();
                    StringMessageElement strMsgElem = new StringMessageElement( Key, Value, null);
                    myMessage.addMessageElement( serviceAdv.getName(), strMsgElem);
                }

                myBiDiPipe.sendMessage(myMessage);

                answer = ANSWER_TIMEOUT;

                return REQ_SENT;
            }
        } catch (URISyntaxException e){
            e.printStackTrace();

            return REQ_INV_PIPE_ID;
        } catch (IOException e) {
            return REQ_INV_PIPE_IO;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return REQ_INV_PIPE_IO;
    }

    public Vector<Entry> getResponse()
    {
        Vector<Entry> results = new Vector<Entry>();

        if (ANSWER_OK != answer)
            return results;

        // This is important: I always call "getState()" and check its value
        // before calling this method, so you can say it's guaranteed to be
        // called after answer = ANSWER_OK, which never happens because of the
        // lock
        for (int i=0; i<output.size(); i++)
            results.add( output.get(i) );

        return results;
    }

    public int getState()
    {
        int count = 10;

        return answer;
    }
}

I always create JxtaCustomer like this:

JxtaCustomer customer = new JxtaCustomer(adv, ThePeer);
Vector<Entry> params = new Vector<Entry>();

params.add( new Entry("key1", "value1") );
params.add( new Entry("key2", "value2") );

customer.sendRequest(params);

while(JxtaCustomer.ANSWER_OK != customer.getState()) {
   // I was actually using synchronized locks and timed waits
   // but since I'm stuck there I'm using this nonsense instead
}

Vector<Entry> response = customer.getResponse();

Here is where in the code I call "sleep", "wait" or I create a "synchronized" (this last one is never)

$ grep synchronized $(find . -name "*.java")

$ grep sleep $(find . -name "*.java")
./Src/net/ubi/jxta/JxtaCustomer.java:                Thread.sleep(500);

$ grep wait $(find . -name "*.java")
./App/ExampleServices/SimpleSigner.java:    boolean waiting = false;
./App/ExampleServices/SimpleSigner.java:        waiting = true;
./App/ExampleServices/SimpleSigner.java:        return (waiting && JxtaCustomer.ANSWER_OK == customer.getState());
./App/ExampleServices/SimpleSigner.java:        if (!waiting)
./App/ExampleServices/SimpleSigner.java:            waiting = false;
./App/ExampleServices/SimpleSigner.java:            return "Error: Response not ready, wait more time\n";

Solution

  • Your constructor sets a local variable called output,and not the member variable, you have

    Vector<Entry> output = new Vector<Entry>();
    

    when it should be

    output = new Vector<Entry>();
    

    So, later on when you call output.clear() you're getting an Null Pointer Exception thrown, which is why the following lines of code don't get executed.

    compiler warnings like "unused local variables" can help spot these sorts of mistakes.