2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 package com
.sun
.star
.lib
.uno
.protocols
.urp
;
21 import com
.sun
.star
.bridge
.InvalidProtocolChangeException
;
22 import com
.sun
.star
.bridge
.ProtocolProperty
;
23 import com
.sun
.star
.bridge
.XProtocolProperties
;
24 import com
.sun
.star
.lang
.DisposedException
;
25 import com
.sun
.star
.lib
.uno
.environments
.remote
.IProtocol
;
26 import com
.sun
.star
.lib
.uno
.environments
.remote
.Message
;
27 import com
.sun
.star
.lib
.uno
.environments
.remote
.ThreadId
;
28 import com
.sun
.star
.lib
.uno
.typedesc
.MethodDescription
;
29 import com
.sun
.star
.lib
.uno
.typedesc
.TypeDescription
;
30 import com
.sun
.star
.uno
.Any
;
31 import com
.sun
.star
.uno
.IBridge
;
32 import com
.sun
.star
.uno
.IMethodDescription
;
33 import com
.sun
.star
.uno
.ITypeDescription
;
34 import com
.sun
.star
.uno
.Type
;
35 import com
.sun
.star
.uno
.TypeClass
;
36 import com
.sun
.star
.uno
.UnoRuntime
;
37 import com
.sun
.star
.uno
.XCurrentContext
;
38 import java
.io
.DataInput
;
39 import java
.io
.DataInputStream
;
40 import java
.io
.DataOutputStream
;
41 import java
.io
.IOException
;
42 import java
.io
.InputStream
;
43 import java
.io
.OutputStream
;
44 import java
.lang
.reflect
.Array
;
45 import java
.util
.ArrayList
;
46 import java
.util
.Random
;
47 import java
.util
.StringTokenizer
;
50 * This class internally relies on the availability of Java UNO type information
51 * for the interface type <code>com.sun.star.bridge.XProtocolProperties</code>,
52 * even though URP itself does not rely on that type.
54 public final class urp
implements IProtocol
{
56 IBridge bridge
, String attributes
, InputStream input
,
59 this.input
= new DataInputStream(input
);
60 this.output
= new DataOutputStream(output
);
61 marshal
= new Marshal(bridge
, CACHE_SIZE
);
62 unmarshal
= new Unmarshal(bridge
, CACHE_SIZE
);
63 forceSynchronous
= parseAttributes(attributes
);
70 public void init() throws IOException
{
71 synchronized (monitor
) {
72 if (state
== STATE_INITIAL0
) {
80 * @see IProtocol#terminate
82 public void terminate() {
83 synchronized (monitor
) {
84 state
= STATE_TERMINATED
;
92 * @see IProtocol#readMessage
94 public Message
readMessage() throws IOException
{
96 if (!unmarshal
.hasMore()) {
97 unmarshal
.reset(readBlock());
98 if (!unmarshal
.hasMore()) {
99 throw new IOException("closeConnection message received");
103 int header
= unmarshal
.read8Bit();
104 if ((header
& HEADER_LONGHEADER
) != 0) {
105 if ((header
& HEADER_REQUEST
) != 0) {
106 msg
= readLongRequest(header
);
108 msg
= readReply(header
);
111 msg
= readShortRequest(header
);
113 if (msg
.isInternal()) {
114 handleInternalMessage(msg
);
123 * @see IProtocol#writeRequest
125 public boolean writeRequest(
126 String oid
, TypeDescription type
, String function
, ThreadId tid
,
130 if (oid
.equals(PROPERTIES_OID
)) {
131 throw new IllegalArgumentException("illegal OID " + oid
);
133 synchronized (monitor
) {
134 while (!initialized
) {
137 } catch (InterruptedException e
) {
138 Thread
.currentThread().interrupt();
139 throw new RuntimeException(e
);
142 if (state
== STATE_TERMINATED
) {
143 throw new DisposedException();
145 return writeRequest(false, oid
, type
, function
, tid
, arguments
);
151 * @see IProtocol#writeReply
153 public void writeReply(boolean exception
, ThreadId tid
, Object result
)
156 synchronized (output
) {
157 writeQueuedReleases();
158 int header
= HEADER_LONGHEADER
;
159 PendingRequests
.Item pending
= pendingIn
.pop(tid
);
160 TypeDescription resultType
;
161 ITypeDescription
[] argTypes
;
164 header
|= HEADER_EXCEPTION
;
165 resultType
= TypeDescription
.getTypeDescription(TypeClass
.ANY
);
169 resultType
= (TypeDescription
)
170 pending
.function
.getReturnSignature();
171 argTypes
= pending
.function
.getOutSignature();
172 args
= pending
.arguments
;
174 if (!tid
.equals(outL1Tid
)) {
175 header
|= HEADER_NEWTID
;
180 marshal
.write8Bit(header
);
182 marshal
.writeThreadId(tid
);
184 marshal
.writeValue(resultType
, result
);
185 if (argTypes
!= null) {
186 for (int i
= 0; i
< argTypes
.length
; ++i
) {
187 if (argTypes
[i
] != null) {
189 (TypeDescription
) argTypes
[i
].getComponentType(),
190 Array
.get(args
[i
], 0));
198 private void sendRequestChange() throws IOException
{
199 if (propertiesTid
== null) {
200 propertiesTid
= ThreadId
.createFresh();
202 random
= new Random().nextInt();
204 true, PROPERTIES_OID
,
205 TypeDescription
.getTypeDescription(XProtocolProperties
.class),
206 PROPERTIES_FUN_REQUEST_CHANGE
, propertiesTid
,
207 new Object
[] { Integer
.valueOf(random
) });
208 state
= STATE_REQUESTED
;
211 private void handleInternalMessage(Message message
) throws IOException
{
212 if (message
.isRequest()) {
213 String t
= message
.getType().getTypeName();
214 if (!t
.equals("com.sun.star.bridge.XProtocolProperties")) {
215 throw new IOException(
216 "read URP protocol properties request with unsupported"
219 int fid
= message
.getMethod().getIndex();
221 case PROPERTIES_FID_REQUEST_CHANGE
:
222 checkSynchronousPropertyRequest(message
);
223 synchronized (monitor
) {
228 false, message
.getThreadId(), Integer
.valueOf(1));
231 case STATE_REQUESTED
:
233 = ((Integer
) message
.getArguments()[0]).intValue();
236 false, message
.getThreadId(), Integer
.valueOf(1));
238 } else if (random
== n
) {
240 false, message
.getThreadId(), Integer
.valueOf(-1));
241 state
= STATE_INITIAL
;
245 false, message
.getThreadId(), Integer
.valueOf(0));
250 true, message
.getThreadId(),
251 new com
.sun
.star
.uno
.RuntimeException(
252 "read URP protocol properties requestChange"
253 + " request in illegal state"));
258 case PROPERTIES_FID_COMMIT_CHANGE
:
259 checkSynchronousPropertyRequest(message
);
260 synchronized (monitor
) {
261 if (state
== STATE_WAIT
) {
262 ProtocolProperty
[] p
= (ProtocolProperty
[])
263 message
.getArguments()[0];
267 for (; i
< p
.length
; ++i
) {
268 if (p
[i
].Name
.equals(PROPERTY_CURRENT_CONTEXT
)) {
276 writeReply(false, message
.getThreadId(), null);
279 true, message
.getThreadId(),
280 new InvalidProtocolChangeException(
283 state
= STATE_INITIAL
;
286 currentContext
= true;
295 true, message
.getThreadId(),
296 new com
.sun
.star
.uno
.RuntimeException(
297 "read URP protocol properties commitChange"
298 + " request in illegal state"));
303 throw new IOException(
304 "read URP protocol properties request with unsupported"
305 + " function ID " + fid
);
308 synchronized (monitor
) {
309 if (state
== STATE_COMMITTED
) {
310 // commitChange reply:
311 if (!message
.isAbnormalTermination()) {
312 currentContext
= true;
314 state
= STATE_INITIAL
;
318 // requestChange reply:
319 if (message
.isAbnormalTermination()) {
320 // remote side probably does not support negotiation:
321 state
= STATE_INITIAL
;
325 int n
= ((Integer
) message
.getResult()).intValue();
332 true, PROPERTIES_OID
,
333 TypeDescription
.getTypeDescription(
334 XProtocolProperties
.class),
335 PROPERTIES_FUN_COMMIT_CHANGE
, propertiesTid
,
337 new ProtocolProperty
[] {
338 new ProtocolProperty(
339 PROPERTY_CURRENT_CONTEXT
,
341 state
= STATE_COMMITTED
;
344 throw new IOException(
345 "read URP protocol properties "
346 + PROPERTIES_FUN_REQUEST_CHANGE
347 + " reply with illegal return value " + n
);
355 private void checkSynchronousPropertyRequest(Message message
)
358 if (!message
.isSynchronous()) {
359 throw new IOException(
360 "read URP protocol properties request for synchronous function"
361 + " marked as not SYNCHRONOUS");
365 private byte[] readBlock() throws IOException
{
366 int size
= input
.readInt();
367 input
.readInt(); // ignore count
368 byte[] bytes
= new byte[size
];
369 input
.readFully(bytes
);
373 private UrpMessage
readLongRequest(int header
) throws IOException
{
374 boolean sync
= false;
375 if ((header
& HEADER_MOREFLAGS
) != 0) {
376 if (unmarshal
.read8Bit() != (HEADER_MUSTREPLY
| HEADER_SYNCHRONOUS
))
378 throw new IOException(
379 "read URP request with bad MUSTREPLY/SYNCHRONOUS byte");
383 int funId
= (header
& HEADER_FUNCTIONID16
) != 0
384 ? unmarshal
.read16Bit() : unmarshal
.read8Bit();
385 if ((header
& HEADER_NEWTYPE
) != 0) {
386 inL1Type
= unmarshal
.readType();
387 if (inL1Type
.getTypeClass() != TypeClass
.INTERFACE
) {
388 throw new IOException(
389 "read URP request with non-interface type " + inL1Type
);
392 if ((header
& HEADER_NEWOID
) != 0) {
393 inL1Oid
= unmarshal
.readObjectId();
395 if ((header
& HEADER_NEWTID
) != 0) {
396 inL1Tid
= unmarshal
.readThreadId();
398 return readRequest(funId
, sync
);
401 private UrpMessage
readShortRequest(int header
) {
402 int funId
= (header
& HEADER_FUNCTIONID14
) != 0
403 ?
((header
& HEADER_FUNCTIONID
) << 8) | unmarshal
.read8Bit()
404 : header
& HEADER_FUNCTIONID
;
405 return readRequest(funId
, false);
408 private UrpMessage
readRequest(int functionId
, boolean forcedSynchronous
) {
409 boolean internal
= PROPERTIES_OID
.equals(inL1Oid
);
410 // inL1Oid may be null in XInstanceProvider.getInstance("")
412 (currentContext
&& !internal
413 && functionId
!= MethodDescription
.ID_RELEASE
)
414 ?
(XCurrentContext
) unmarshal
.readInterface(
415 new Type(XCurrentContext
.class))
417 IMethodDescription desc
= inL1Type
.getMethodDescription(functionId
);
418 ITypeDescription
[] inSig
= desc
.getInSignature();
419 ITypeDescription
[] outSig
= desc
.getOutSignature();
420 Object
[] args
= new Object
[inSig
.length
];
421 for (int i
= 0; i
< args
.length
; ++i
) {
422 if (inSig
[i
] != null) {
423 if (outSig
[i
] != null) {
424 Object inout
= Array
.newInstance(
425 outSig
[i
].getComponentType().getZClass(), 1);
429 (TypeDescription
) outSig
[i
].getComponentType()));
432 args
[i
] = unmarshal
.readValue((TypeDescription
) inSig
[i
]);
435 args
[i
] = Array
.newInstance(
436 outSig
[i
].getComponentType().getZClass(), 1);
439 boolean sync
= forcedSynchronous
|| !desc
.isOneway();
442 inL1Tid
, new PendingRequests
.Item(internal
, desc
, args
));
444 return new UrpMessage(
445 inL1Tid
, true, inL1Oid
, inL1Type
, desc
, sync
, cc
, false, null, args
,
449 private UrpMessage
readReply(int header
) {
450 if ((header
& HEADER_NEWTID
) != 0) {
451 inL1Tid
= unmarshal
.readThreadId();
453 PendingRequests
.Item pending
= pendingOut
.pop(inL1Tid
);
454 TypeDescription resultType
;
455 ITypeDescription
[] argTypes
;
457 boolean exception
= (header
& HEADER_EXCEPTION
) != 0;
459 resultType
= TypeDescription
.getTypeDescription(TypeClass
.ANY
);
463 resultType
= (TypeDescription
)
464 pending
.function
.getReturnSignature();
465 argTypes
= pending
.function
.getOutSignature();
466 args
= pending
.arguments
;
468 Object result
= resultType
== null
469 ?
null : unmarshal
.readValue(resultType
);
470 if (argTypes
!= null) {
471 for (int i
= 0; i
< argTypes
.length
; ++i
) {
472 if (argTypes
[i
] != null) {
476 (TypeDescription
) argTypes
[i
].getComponentType()));
480 return new UrpMessage(
481 inL1Tid
, false, null, null, null, false, null, exception
, result
,
482 args
, pending
.internal
);
485 private boolean writeRequest(
486 boolean internal
, String oid
, TypeDescription type
, String function
,
487 ThreadId tid
, Object
[] arguments
)
490 IMethodDescription desc
= type
.getMethodDescription(function
);
491 synchronized (output
) {
492 if (desc
.getIndex() == MethodDescription
.ID_RELEASE
493 && releaseQueue
.size() < MAX_RELEASE_QUEUE_SIZE
)
496 new QueuedRelease(internal
, oid
, type
, desc
, tid
));
499 writeQueuedReleases();
501 internal
, oid
, type
, desc
, tid
, arguments
, true);
506 private boolean writeRequest(
507 boolean internal
, String oid
, TypeDescription type
,
508 IMethodDescription desc
, ThreadId tid
, Object
[] arguments
,
512 int funId
= desc
.getIndex();
513 if (funId
< 0 || funId
> MAX_FUNCTIONID16
) {
514 throw new IllegalArgumentException(
515 "function ID " + funId
+ " out of range");
517 boolean forceSync
= forceSynchronous
518 && funId
!= MethodDescription
.ID_RELEASE
;
519 boolean moreFlags
= forceSync
&& desc
.isOneway();
520 boolean longHeader
= moreFlags
;
522 if (!type
.equals(outL1Type
)) {
524 header
|= HEADER_NEWTYPE
;
529 if (!oid
.equals(outL1Oid
)) {
531 header
|= HEADER_NEWOID
;
536 if (!tid
.equals(outL1Tid
)) {
538 header
|= HEADER_NEWTID
;
543 if (funId
> MAX_FUNCTIONID14
) {
547 header
|= HEADER_LONGHEADER
| HEADER_REQUEST
;
548 if (funId
> MAX_FUNCTIONID8
) {
549 header
|= HEADER_FUNCTIONID16
;
552 header
|= HEADER_MOREFLAGS
;
554 marshal
.write8Bit(header
);
556 marshal
.write8Bit(HEADER_MUSTREPLY
| HEADER_SYNCHRONOUS
);
558 if (funId
> MAX_FUNCTIONID8
) {
559 marshal
.write16Bit(funId
);
561 marshal
.write8Bit(funId
);
564 marshal
.writeType(type
);
567 marshal
.writeObjectId(oid
);
570 marshal
.writeThreadId(tid
);
573 if (funId
> HEADER_FUNCTIONID
) {
574 marshal
.write8Bit(HEADER_FUNCTIONID14
| (funId
>> 8));
576 marshal
.write8Bit(funId
);
578 if (currentContext
&& !internal
579 && funId
!= MethodDescription
.ID_RELEASE
)
581 marshal
.writeInterface(
582 UnoRuntime
.getCurrentContext(),
583 new Type(XCurrentContext
.class));
585 ITypeDescription
[] inSig
= desc
.getInSignature();
586 ITypeDescription
[] outSig
= desc
.getOutSignature();
587 for (int i
= 0; i
< inSig
.length
; ++i
) {
588 if (inSig
[i
] != null) {
589 if (outSig
[i
] != null) {
591 (TypeDescription
) outSig
[i
].getComponentType(),
592 ((Object
[]) arguments
[i
])[0]);
595 (TypeDescription
) inSig
[i
], arguments
[i
]);
599 boolean sync
= forceSync
|| !desc
.isOneway();
602 outL1Tid
, new PendingRequests
.Item(internal
, desc
, arguments
));
608 private void writeBlock(boolean flush
) throws IOException
{
609 byte[] data
= marshal
.reset();
610 output
.writeInt(data
.length
);
618 private void writeQueuedReleases() throws IOException
{
619 for (int i
= releaseQueue
.size(); i
> 0;) {
621 QueuedRelease r
= releaseQueue
.get(i
);
623 r
.internal
, r
.objectId
, r
.type
, r
.method
, r
.threadId
, null,
625 releaseQueue
.remove(i
);
629 private static boolean parseAttributes(String attributes
) {
630 boolean forceSynchronous
= true;
631 if (attributes
!= null) {
632 StringTokenizer t
= new StringTokenizer(attributes
, ",");
633 while (t
.hasMoreTokens()) {
634 String a
= t
.nextToken();
636 int i
= a
.indexOf('=');
638 v
= a
.substring(i
+ 1);
639 a
= a
.substring(0, i
);
641 if (a
.equalsIgnoreCase("ForceSynchronous")) {
642 forceSynchronous
= parseBooleanAttributeValue(a
, v
);
643 } else if (a
.equalsIgnoreCase("negotiate")) {
645 parseBooleanAttributeValue(a
, v
);
647 throw new IllegalArgumentException(
648 "unknown protocol attribute " + a
);
652 return forceSynchronous
;
655 private static boolean parseBooleanAttributeValue(
656 String attribute
, String value
)
659 throw new IllegalArgumentException(
660 "missing value for protocol attribute " + attribute
);
662 if (value
.equals("0")) {
664 } else if (value
.equals("1")) {
667 throw new IllegalArgumentException(
668 "bad value " + value
+ " for protocol attribute " + attribute
);
672 private static final class QueuedRelease
{
673 public QueuedRelease(
674 boolean internal
, String objectId
, TypeDescription type
,
675 IMethodDescription method
, ThreadId threadId
)
677 this.internal
= internal
;
678 this.objectId
= objectId
;
680 this.method
= method
;
681 this.threadId
= threadId
;
684 public final boolean internal
;
685 public final String objectId
;
686 public final TypeDescription type
;
687 public final IMethodDescription method
;
688 public final ThreadId threadId
;
691 private static final String PROPERTIES_OID
= "UrpProtocolProperties";
692 private static final int PROPERTIES_FID_REQUEST_CHANGE
= 4;
693 private static final String PROPERTIES_FUN_REQUEST_CHANGE
= "requestChange";
694 private static final int PROPERTIES_FID_COMMIT_CHANGE
= 5;
695 private static final String PROPERTIES_FUN_COMMIT_CHANGE
= "commitChange";
696 private static final String PROPERTY_CURRENT_CONTEXT
= "CurrentContext";
698 private static final short CACHE_SIZE
= 256;
700 private static final int HEADER_LONGHEADER
= 0x80;
701 private static final int HEADER_REQUEST
= 0x40;
702 private static final int HEADER_NEWTYPE
= 0x20;
703 private static final int HEADER_NEWOID
= 0x10;
704 private static final int HEADER_NEWTID
= 0x08;
705 private static final int HEADER_FUNCTIONID16
= 0x04;
706 private static final int HEADER_MOREFLAGS
= 0x01;
707 private static final int HEADER_MUSTREPLY
= 0x80;
708 private static final int HEADER_SYNCHRONOUS
= 0x40;
709 private static final int HEADER_FUNCTIONID14
= 0x40;
710 private static final int HEADER_FUNCTIONID
= 0x3F;
711 private static final int HEADER_EXCEPTION
= 0x20;
713 private static final int MAX_FUNCTIONID16
= 0xFFFF;
714 private static final int MAX_FUNCTIONID14
= 0x3FFF;
715 private static final int MAX_FUNCTIONID8
= 0xFF;
717 private static final int STATE_INITIAL0
= 0;
718 private static final int STATE_INITIAL
= 1;
719 private static final int STATE_REQUESTED
= 2;
720 private static final int STATE_COMMITTED
= 3;
721 private static final int STATE_WAIT
= 4;
722 private static final int STATE_TERMINATED
= 5;
724 private static final int MAX_RELEASE_QUEUE_SIZE
= 100;
726 private final DataInput input
;
727 private final DataOutputStream output
;
729 private final Marshal marshal
;
730 private final Unmarshal unmarshal
;
732 private final boolean forceSynchronous
;
734 private final PendingRequests pendingIn
= new PendingRequests();
735 private final PendingRequests pendingOut
= new PendingRequests();
737 private final Object monitor
= new Object();
738 private int state
= STATE_INITIAL0
;
739 private boolean initialized
= false;
740 private ThreadId propertiesTid
= null;
742 private boolean currentContext
= false;
744 private ThreadId inL1Tid
= null;
745 private String inL1Oid
= null;
746 private TypeDescription inL1Type
= null;
748 private ThreadId outL1Tid
= null;
749 private String outL1Oid
= null;
750 private ITypeDescription outL1Type
= null;
752 private final ArrayList
<QueuedRelease
> releaseQueue
= new ArrayList
<QueuedRelease
>(); // of QueuedRelease