1 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 package com.sun.star.lib.uno.protocols.urp;
21 
22 import com.sun.star.bridge.InvalidProtocolChangeException;
23 import com.sun.star.bridge.ProtocolProperty;
24 import com.sun.star.bridge.XProtocolProperties;
25 import com.sun.star.lang.DisposedException;
26 import com.sun.star.lib.uno.environments.remote.IProtocol;
27 import com.sun.star.lib.uno.environments.remote.Message;
28 import com.sun.star.lib.uno.environments.remote.ThreadId;
29 import com.sun.star.lib.uno.typedesc.MethodDescription;
30 import com.sun.star.lib.uno.typedesc.TypeDescription;
31 import com.sun.star.uno.Any;
32 import com.sun.star.uno.IBridge;
33 import com.sun.star.uno.Type;
34 import com.sun.star.uno.TypeClass;
35 import com.sun.star.uno.UnoRuntime;
36 import com.sun.star.uno.XCurrentContext;
37 import java.io.DataInput;
38 import java.io.DataInputStream;
39 import java.io.DataOutputStream;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.io.OutputStream;
43 import java.lang.reflect.Array;
44 import java.util.ArrayList;
45 import java.util.Random;
46 import java.util.StringTokenizer;
47 
48 /**
49  * This class internally relies on the availability of Java UNO type information
50  * for the interface type <code>com.sun.star.bridge.XProtocolProperties</code>,
51  * even though URP itself does not rely on that type.
52  */
53 public final class urp implements IProtocol {
urp( IBridge bridge, String attributes, InputStream input, OutputStream output)54     public urp(
55         IBridge bridge, String attributes, InputStream input,
56         OutputStream output)
57     {
58         this.input = new DataInputStream(input);
59         this.output = new DataOutputStream(output);
60         marshal = new Marshal(bridge, CACHE_SIZE);
61         unmarshal = new Unmarshal(bridge, CACHE_SIZE);
62         forceSynchronous = parseAttributes(attributes);
63     }
64 
65     /**
66      *
67      * @see IProtocol#init
68      */
init()69     public void init() throws IOException {
70         synchronized (monitor) {
71             if (state == STATE_INITIAL0) {
72                 sendRequestChange();
73             }
74         }
75     }
76 
77     /**
78      *
79      * @see IProtocol#terminate
80      */
terminate()81     public void terminate() {
82         synchronized (monitor) {
83             state = STATE_TERMINATED;
84             initialized = true;
85             monitor.notifyAll();
86         }
87     }
88 
89     /**
90      *
91      * @see IProtocol#readMessage
92      */
readMessage()93     public Message readMessage() throws IOException {
94         for (;;) {
95             if (!unmarshal.hasMore()) {
96                 unmarshal.reset(readBlock());
97                 if (!unmarshal.hasMore()) {
98                     throw new IOException("closeConnection message received");
99                 }
100             }
101             UrpMessage msg;
102             int header = unmarshal.read8Bit();
103             if ((header & HEADER_LONGHEADER) != 0) {
104                 if ((header & HEADER_REQUEST) != 0) {
105                     msg = readLongRequest(header);
106                 } else {
107                     msg = readReply(header);
108                 }
109             } else {
110                 msg = readShortRequest(header);
111             }
112             if (msg.isInternal()) {
113                 handleInternalMessage(msg);
114             } else {
115                 return msg;
116             }
117         }
118     }
119 
120     /**
121      *
122      * @see IProtocol#writeRequest
123      */
writeRequest( String oid, TypeDescription type, String function, ThreadId tid, Object[] arguments)124     public boolean writeRequest(
125         String oid, TypeDescription type, String function, ThreadId tid,
126         Object[] arguments)
127         throws IOException
128     {
129         if (oid.equals(PROPERTIES_OID)) {
130             throw new IllegalArgumentException("illegal OID " + oid);
131         }
132         synchronized (monitor) {
133             while (!initialized) {
134                 try {
135                     monitor.wait();
136                 } catch (InterruptedException e) {
137                     Thread.currentThread().interrupt();
138                     throw new RuntimeException(e);
139                 }
140             }
141             if (state == STATE_TERMINATED) {
142                 throw new DisposedException();
143             }
144             return writeRequest(false, oid, type, function, tid, arguments);
145         }
146     }
147 
148     /**
149      *
150      * @see IProtocol#writeReply
151      */
writeReply(boolean exception, ThreadId tid, Object result)152     public void writeReply(boolean exception, ThreadId tid, Object result)
153         throws IOException
154     {
155         synchronized (output) {
156             writeQueuedReleases();
157             int header = HEADER_LONGHEADER;
158             PendingRequests.Item pending = pendingIn.pop(tid);
159             TypeDescription resultType;
160             TypeDescription[] argTypes;
161             Object[] args;
162             if (exception) {
163                 header |= HEADER_EXCEPTION;
164                 resultType = TypeDescription.getTypeDescription(TypeClass.ANY);
165                 argTypes = null;
166                 args = null;
167             } else {
168                 resultType = pending.function.getReturnSignature();
169                 argTypes = pending.function.getOutSignature();
170                 args = pending.arguments;
171             }
172             if (!tid.equals(outL1Tid)) {
173                 header |= HEADER_NEWTID;
174                 outL1Tid = tid;
175             } else {
176                 tid = null;
177             }
178             marshal.write8Bit(header);
179             if (tid != null) {
180                 marshal.writeThreadId(tid);
181             }
182             marshal.writeValue(resultType, result);
183             if (argTypes != null) {
184                 for (int i = 0; i < argTypes.length; ++i) {
185                     if (argTypes[i] != null) {
186                         marshal.writeValue(
187                             argTypes[i].getComponentType(),
188                             Array.get(args[i], 0));
189                     }
190                 }
191             }
192             writeBlock(true);
193         }
194     }
195 
sendRequestChange()196     private void sendRequestChange() throws IOException {
197         if (propertiesTid == null) {
198             propertiesTid = ThreadId.createFresh();
199         }
200         random = randomGenerator.nextInt();
201         writeRequest(
202             true, PROPERTIES_OID,
203             TypeDescription.getTypeDescription(XProtocolProperties.class),
204             PROPERTIES_FUN_REQUEST_CHANGE, propertiesTid,
205             new Object[] { Integer.valueOf(random) });
206         state = STATE_REQUESTED;
207     }
208 
handleInternalMessage(Message message)209     private void handleInternalMessage(Message message) throws IOException {
210         if (message.isRequest()) {
211             String t = message.getType().getTypeName();
212             if (!t.equals("com.sun.star.bridge.XProtocolProperties")) {
213                 throw new IOException(
214                     "read URP protocol properties request with unsupported"
215                     + " type " + t);
216             }
217             int fid = message.getMethod().getIndex();
218             switch (fid) {
219             case PROPERTIES_FID_REQUEST_CHANGE:
220                 checkSynchronousPropertyRequest(message);
221                 synchronized (monitor) {
222                     switch (state) {
223                     case STATE_INITIAL0:
224                     case STATE_INITIAL:
225                         writeReply(
226                             false, message.getThreadId(), Integer.valueOf(1));
227                         state = STATE_WAIT;
228                         break;
229                     case STATE_REQUESTED:
230                         int n
231                             = ((Integer) message.getArguments()[0]).intValue();
232                         if (random < n) {
233                             writeReply(
234                                 false, message.getThreadId(), Integer.valueOf(1));
235                             state = STATE_WAIT;
236                         } else if (random == n) {
237                             writeReply(
238                                 false, message.getThreadId(), Integer.valueOf(-1));
239                             state = STATE_INITIAL;
240                             sendRequestChange();
241                         } else {
242                             writeReply(
243                                 false, message.getThreadId(), Integer.valueOf(0));
244                         }
245                         break;
246                     default:
247                         writeReply(
248                             true, message.getThreadId(),
249                             new com.sun.star.uno.RuntimeException(
250                                 "read URP protocol properties requestChange"
251                                 + " request in illegal state"));
252                         break;
253                     }
254                 }
255                 break;
256             case PROPERTIES_FID_COMMIT_CHANGE:
257                 checkSynchronousPropertyRequest(message);
258                 synchronized (monitor) {
259                     if (state == STATE_WAIT) {
260                         ProtocolProperty[] p = (ProtocolProperty[])
261                             message.getArguments()[0];
262                         boolean ok = true;
263                         boolean cc = false;
264                         int i = 0;
265                         for (; i < p.length; ++i) {
266                             if (p[i].Name.equals(PROPERTY_CURRENT_CONTEXT)) {
267                                 cc = true;
268                             } else {
269                                 ok = false;
270                                 break;
271                             }
272                         }
273                         if (ok) {
274                             writeReply(false, message.getThreadId(), null);
275                         } else {
276                             writeReply(
277                                 true, message.getThreadId(),
278                                 new InvalidProtocolChangeException(
279                                     "", null, p[i], 1));
280                         }
281                         state = STATE_INITIAL;
282                         if (!initialized) {
283                             if (cc) {
284                                 currentContext = true;
285                                 initialized = true;
286                                 monitor.notifyAll();
287                             } else {
288                                 sendRequestChange();
289                             }
290                         }
291                     } else {
292                         writeReply(
293                             true, message.getThreadId(),
294                             new com.sun.star.uno.RuntimeException(
295                                 "read URP protocol properties commitChange"
296                                 + " request in illegal state"));
297                     }
298                 }
299                 break;
300             default:
301                 throw new IOException(
302                     "read URP protocol properties request with unsupported"
303                     + " function ID " + fid);
304             }
305         } else {
306             synchronized (monitor) {
307                 if (state == STATE_COMMITTED) {
308                     // commitChange reply:
309                     if (!message.isAbnormalTermination()) {
310                         currentContext = true;
311                     }
312                     state = STATE_INITIAL;
313                     initialized = true;
314                     monitor.notifyAll();
315                 } else {
316                     // requestChange reply:
317                     if (message.isAbnormalTermination()) {
318                         // remote side probably does not support negotiation:
319                         state = STATE_INITIAL;
320                         initialized = true;
321                         monitor.notifyAll();
322                     } else {
323                         int n = ((Integer) message.getResult()).intValue();
324                         switch (n) {
325                         case -1:
326                         case 0:
327                             break;
328                         case 1:
329                             writeRequest(
330                                 true, PROPERTIES_OID,
331                                 TypeDescription.getTypeDescription(
332                                     XProtocolProperties.class),
333                                 PROPERTIES_FUN_COMMIT_CHANGE, propertiesTid,
334                                 new Object[] {
335                                     new ProtocolProperty[] {
336                                         new ProtocolProperty(
337                                             PROPERTY_CURRENT_CONTEXT,
338                                             Any.VOID) } });
339                             state = STATE_COMMITTED;
340                             break;
341                         default:
342                             throw new IOException(
343                                 "read URP protocol properties "
344                                 + PROPERTIES_FUN_REQUEST_CHANGE
345                                 + " reply with illegal return value " + n);
346                         }
347                     }
348                 }
349             }
350         }
351     }
352 
checkSynchronousPropertyRequest(Message message)353     private void checkSynchronousPropertyRequest(Message message)
354         throws IOException
355     {
356         if (!message.isSynchronous()) {
357             throw new IOException(
358                 "read URP protocol properties request for synchronous function"
359                 + " marked as not SYNCHRONOUS");
360         }
361     }
362 
readBlock()363     private byte[] readBlock() throws IOException {
364         int size = input.readInt();
365         input.readInt(); // ignore count
366         byte[] bytes = new byte[size];
367         input.readFully(bytes);
368         return bytes;
369     }
370 
readLongRequest(int header)371     private UrpMessage readLongRequest(int header) throws IOException {
372         boolean sync = false;
373         if ((header & HEADER_MOREFLAGS) != 0) {
374             if (unmarshal.read8Bit() != (HEADER_MUSTREPLY | HEADER_SYNCHRONOUS))
375             {
376                 throw new IOException(
377                     "read URP request with bad MUSTREPLY/SYNCHRONOUS byte");
378             }
379             sync = true;
380         }
381         int funId = (header & HEADER_FUNCTIONID16) != 0
382             ? unmarshal.read16Bit() : unmarshal.read8Bit();
383         if ((header & HEADER_NEWTYPE) != 0) {
384             inL1Type = unmarshal.readType();
385             if (inL1Type.getTypeClass() != TypeClass.INTERFACE) {
386                 throw new IOException(
387                     "read URP request with non-interface type " + inL1Type);
388             }
389         }
390         if ((header & HEADER_NEWOID) != 0) {
391             inL1Oid = unmarshal.readObjectId();
392         }
393         if ((header & HEADER_NEWTID) != 0) {
394             inL1Tid = unmarshal.readThreadId();
395         }
396         return readRequest(funId, sync);
397     }
398 
readShortRequest(int header)399     private UrpMessage readShortRequest(int header) throws IOException {
400         int funId = (header & HEADER_FUNCTIONID14) != 0
401             ? ((header & HEADER_FUNCTIONID) << 8) | unmarshal.read8Bit()
402             : header & HEADER_FUNCTIONID;
403         return readRequest(funId, false);
404     }
405 
readRequest(int functionId, boolean forcedSynchronous)406     private UrpMessage readRequest(int functionId, boolean forcedSynchronous)
407         throws IOException
408     {
409         boolean internal = PROPERTIES_OID.equals(inL1Oid);
410             // inL1Oid may be null in XInstanceProvider.getInstance("")
411         XCurrentContext cc =
412             (currentContext && !internal
413              && functionId != MethodDescription.ID_RELEASE)
414             ? (XCurrentContext) unmarshal.readInterface(
415                 new Type(XCurrentContext.class))
416             : null;
417         MethodDescription desc = inL1Type.getMethodDescription(functionId);
418         if (desc == null) {
419             throw new IOException(
420                 "read URP request with unsupported function ID " + functionId);
421         }
422         TypeDescription[] inSig = desc.getInSignature();
423         TypeDescription[] outSig = desc.getOutSignature();
424         Object[] args = new Object[inSig.length];
425         for (int i = 0; i < args.length; ++i) {
426             if (inSig[i] != null) {
427                 if (outSig[i] != null) {
428                     Object inout = Array.newInstance(
429                         outSig[i].getComponentType().getZClass(), 1);
430                     Array.set(
431                         inout, 0,
432                         unmarshal.readValue(
433                             outSig[i].getComponentType()));
434                     args[i] = inout;
435                 } else {
436                     args[i] = unmarshal.readValue(inSig[i]);
437                 }
438             } else {
439                 args[i] = Array.newInstance(
440                     outSig[i].getComponentType().getZClass(), 1);
441             }
442         }
443         boolean sync = forcedSynchronous || !desc.isOneway();
444         if (sync) {
445             pendingIn.push(
446                 inL1Tid, new PendingRequests.Item(internal, desc, args));
447         }
448         return new UrpMessage(
449             inL1Tid, true, inL1Oid, inL1Type, desc, sync, cc, false, null, args,
450             internal);
451     }
452 
readReply(int header)453     private UrpMessage readReply(int header) {
454         if ((header & HEADER_NEWTID) != 0) {
455             inL1Tid = unmarshal.readThreadId();
456         }
457         PendingRequests.Item pending = pendingOut.pop(inL1Tid);
458         TypeDescription resultType;
459         TypeDescription[] argTypes;
460         Object[] args;
461         boolean exception = (header & HEADER_EXCEPTION) != 0;
462         if (exception) {
463             resultType = TypeDescription.getTypeDescription(TypeClass.ANY);
464             argTypes = null;
465             args = null;
466         } else {
467             resultType = pending.function.getReturnSignature();
468             argTypes = pending.function.getOutSignature();
469             args = pending.arguments;
470         }
471         Object result = resultType == null
472             ? null : unmarshal.readValue(resultType);
473         if (argTypes != null) {
474             for (int i = 0; i < argTypes.length; ++i) {
475                 if (argTypes[i] != null) {
476                     Array.set(
477                         args[i], 0,
478                         unmarshal.readValue(
479                             argTypes[i].getComponentType()));
480                 }
481             }
482         }
483         return new UrpMessage(
484             inL1Tid, false, null, null, null, false, null, exception, result,
485             args, pending.internal);
486     }
487 
writeRequest( boolean internal, String oid, TypeDescription type, String function, ThreadId tid, Object[] arguments)488     private boolean writeRequest(
489         boolean internal, String oid, TypeDescription type, String function,
490         ThreadId tid, Object[] arguments)
491         throws IOException
492     {
493         MethodDescription desc = type.getMethodDescription(function);
494         synchronized (output) {
495             if (desc.getIndex() == MethodDescription.ID_RELEASE
496                 && releaseQueue.size() < MAX_RELEASE_QUEUE_SIZE)
497             {
498                 releaseQueue.add(
499                     new QueuedRelease(internal, oid, type, desc, tid));
500                 return false;
501             } else {
502                 writeQueuedReleases();
503                 return writeRequest(
504                     internal, oid, type, desc, tid, arguments, true);
505             }
506         }
507     }
508 
writeRequest( boolean internal, String oid, TypeDescription type, MethodDescription desc, ThreadId tid, Object[] arguments, boolean flush)509     private boolean writeRequest(
510         boolean internal, String oid, TypeDescription type,
511         MethodDescription desc, ThreadId tid, Object[] arguments,
512         boolean flush)
513         throws IOException
514     {
515         int funId = desc.getIndex();
516         if (funId < 0 || funId > MAX_FUNCTIONID16) {
517             throw new IllegalArgumentException(
518                 "function ID " + funId + " out of range");
519         }
520         boolean forceSync = forceSynchronous
521             && funId != MethodDescription.ID_RELEASE;
522         boolean moreFlags = forceSync && desc.isOneway();
523         boolean longHeader = moreFlags;
524         int header = 0;
525         if (!type.equals(outL1Type)) {
526             longHeader = true;
527             header |= HEADER_NEWTYPE;
528             outL1Type = type;
529         } else {
530             type = null;
531         }
532         if (!oid.equals(outL1Oid)) {
533             longHeader = true;
534             header |= HEADER_NEWOID;
535             outL1Oid = oid;
536         } else {
537             oid = null;
538         }
539         if (!tid.equals(outL1Tid)) {
540             longHeader = true;
541             header |= HEADER_NEWTID;
542             outL1Tid = tid;
543         } else {
544             tid = null;
545         }
546         if (funId > MAX_FUNCTIONID14) {
547             longHeader = true;
548         }
549         if (longHeader) {
550             header |= HEADER_LONGHEADER | HEADER_REQUEST;
551             if (funId > MAX_FUNCTIONID8) {
552                 header |= HEADER_FUNCTIONID16;
553             }
554             if (moreFlags) {
555                 header |= HEADER_MOREFLAGS;
556             }
557             marshal.write8Bit(header);
558             if (moreFlags) {
559                 marshal.write8Bit(HEADER_MUSTREPLY | HEADER_SYNCHRONOUS);
560             }
561             if (funId > MAX_FUNCTIONID8) {
562                 marshal.write16Bit(funId);
563             } else {
564                 marshal.write8Bit(funId);
565             }
566             if (type != null) {
567                 marshal.writeType(type);
568             }
569             if (oid != null) {
570                 marshal.writeObjectId(oid);
571             }
572             if (tid != null) {
573                 marshal.writeThreadId(tid);
574             }
575         } else {
576             if (funId > HEADER_FUNCTIONID) {
577                 marshal.write8Bit(HEADER_FUNCTIONID14 | (funId >> 8));
578             }
579             marshal.write8Bit(funId);
580         }
581         if (currentContext && !internal
582             && funId != MethodDescription.ID_RELEASE)
583         {
584             marshal.writeInterface(
585                 UnoRuntime.getCurrentContext(),
586                 new Type(XCurrentContext.class));
587         }
588         TypeDescription[] inSig = desc.getInSignature();
589         TypeDescription[] outSig = desc.getOutSignature();
590         for (int i = 0; i < inSig.length; ++i) {
591             if (inSig[i] != null) {
592                 if (outSig[i] != null) {
593                     marshal.writeValue(
594                         outSig[i].getComponentType(),
595                         ((Object[]) arguments[i])[0]);
596                 } else {
597                     marshal.writeValue(
598                         inSig[i], arguments[i]);
599                 }
600             }
601         }
602         boolean sync = forceSync || !desc.isOneway();
603         if (sync) {
604             pendingOut.push(
605                 outL1Tid, new PendingRequests.Item(internal, desc, arguments));
606         }
607         writeBlock(flush);
608         return sync;
609     }
610 
writeBlock(boolean flush)611     private void writeBlock(boolean flush) throws IOException {
612         byte[] data = marshal.reset();
613         output.writeInt(data.length);
614         output.writeInt(1);
615         output.write(data);
616         if (flush) {
617             output.flush();
618         }
619     }
620 
writeQueuedReleases()621     private void writeQueuedReleases() throws IOException {
622         for (int i = releaseQueue.size(); i > 0;) {
623             --i;
624             QueuedRelease r = releaseQueue.get(i);
625             writeRequest(
626                 r.internal, r.objectId, r.type, r.method, r.threadId, null,
627                 false);
628             releaseQueue.remove(i);
629         }
630     }
631 
parseAttributes(String attributes)632     private static boolean parseAttributes(String attributes) {
633         boolean forceSynchronous = true;
634         if (attributes != null) {
635             StringTokenizer t = new StringTokenizer(attributes, ",");
636             while (t.hasMoreTokens()) {
637                 String a = t.nextToken();
638                 String v = null;
639                 int i = a.indexOf('=');
640                 if (i >= 0) {
641                     v = a.substring(i + 1);
642                     a = a.substring(0, i);
643                 }
644                 if (a.equalsIgnoreCase("ForceSynchronous")) {
645                     forceSynchronous = parseBooleanAttributeValue(a, v);
646                 } else if (a.equalsIgnoreCase("negotiate")) {
647                     // Ignored:
648                     parseBooleanAttributeValue(a, v);
649                 } else {
650                     throw new IllegalArgumentException(
651                         "unknown protocol attribute " + a);
652                 }
653             }
654         }
655         return forceSynchronous;
656     }
657 
parseBooleanAttributeValue( String attribute, String value)658     private static boolean parseBooleanAttributeValue(
659         String attribute, String value)
660     {
661         if (value == null) {
662             throw new IllegalArgumentException(
663                 "missing value for protocol attribute " + attribute);
664         }
665         if (value.equals("0")) {
666             return false;
667         } else if (value.equals("1")) {
668             return true;
669         } else {
670             throw new IllegalArgumentException(
671                 "bad value " + value + " for protocol attribute " + attribute);
672         }
673     }
674 
675     private static final class QueuedRelease {
QueuedRelease( boolean internal, String objectId, TypeDescription type, MethodDescription method, ThreadId threadId)676         public QueuedRelease(
677             boolean internal, String objectId, TypeDescription type,
678             MethodDescription method, ThreadId threadId)
679         {
680             this.internal = internal;
681             this.objectId = objectId;
682             this.type = type;
683             this.method = method;
684             this.threadId = threadId;
685         }
686 
687         public final boolean internal;
688         public final String objectId;
689         public final TypeDescription type;
690         public final MethodDescription method;
691         public final ThreadId threadId;
692     }
693 
694     private static final String PROPERTIES_OID = "UrpProtocolProperties";
695     private static final int PROPERTIES_FID_REQUEST_CHANGE = 4;
696     private static final String PROPERTIES_FUN_REQUEST_CHANGE = "requestChange";
697     private static final int PROPERTIES_FID_COMMIT_CHANGE = 5;
698     private static final String PROPERTIES_FUN_COMMIT_CHANGE = "commitChange";
699     private static final String PROPERTY_CURRENT_CONTEXT = "CurrentContext";
700 
701     private static final short CACHE_SIZE = 256;
702 
703     private static final int HEADER_LONGHEADER = 0x80;
704     private static final int HEADER_REQUEST = 0x40;
705     private static final int HEADER_NEWTYPE = 0x20;
706     private static final int HEADER_NEWOID = 0x10;
707     private static final int HEADER_NEWTID = 0x08;
708     private static final int HEADER_FUNCTIONID16 = 0x04;
709     private static final int HEADER_MOREFLAGS = 0x01;
710     private static final int HEADER_MUSTREPLY = 0x80;
711     private static final int HEADER_SYNCHRONOUS = 0x40;
712     private static final int HEADER_FUNCTIONID14 = 0x40;
713     private static final int HEADER_FUNCTIONID = 0x3F;
714     private static final int HEADER_EXCEPTION = 0x20;
715 
716     private static final int MAX_FUNCTIONID16 = 0xFFFF;
717     private static final int MAX_FUNCTIONID14 = 0x3FFF;
718     private static final int MAX_FUNCTIONID8 = 0xFF;
719 
720     private static final int STATE_INITIAL0 = 0;
721     private static final int STATE_INITIAL = 1;
722     private static final int STATE_REQUESTED = 2;
723     private static final int STATE_COMMITTED = 3;
724     private static final int STATE_WAIT = 4;
725     private static final int STATE_TERMINATED = 5;
726 
727     private static final int MAX_RELEASE_QUEUE_SIZE = 100;
728 
729     private static final Random randomGenerator = new Random();
730 
731     private final DataInput input;
732     private final DataOutputStream output;
733 
734     private final Marshal marshal;
735     private final Unmarshal unmarshal;
736 
737     private final boolean forceSynchronous;
738 
739     private final PendingRequests pendingIn = new PendingRequests();
740     private final PendingRequests pendingOut = new PendingRequests();
741 
742     private final Object monitor = new Object();
743     private int state = STATE_INITIAL0;
744     private boolean initialized = false;
745     private ThreadId propertiesTid = null;
746     private int random;
747     private boolean currentContext = false;
748 
749     private ThreadId inL1Tid = null;
750     private String inL1Oid = null;
751     private TypeDescription inL1Type = null;
752 
753     private ThreadId outL1Tid = null;
754     private String outL1Oid = null;
755     private TypeDescription outL1Type = null;
756 
757     private final ArrayList<QueuedRelease> releaseQueue = new ArrayList<QueuedRelease>(); // of QueuedRelease
758 }
759 
760 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
761