1 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 package com
.sun
.star
.lib
.uno
.protocols
.urp
;
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
;
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.
53 public final class urp
implements IProtocol
{
55 IBridge bridge
, String attributes
, InputStream input
,
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
);
69 public void init() throws IOException
{
70 synchronized (monitor
) {
71 if (state
== STATE_INITIAL0
) {
79 * @see IProtocol#terminate
81 public void terminate() {
82 synchronized (monitor
) {
83 state
= STATE_TERMINATED
;
91 * @see IProtocol#readMessage
93 public Message
readMessage() throws IOException
{
95 if (!unmarshal
.hasMore()) {
96 unmarshal
.reset(readBlock());
97 if (!unmarshal
.hasMore()) {
98 throw new IOException("closeConnection message received");
102 int header
= unmarshal
.read8Bit();
103 if ((header
& HEADER_LONGHEADER
) != 0) {
104 if ((header
& HEADER_REQUEST
) != 0) {
105 msg
= readLongRequest(header
);
107 msg
= readReply(header
);
110 msg
= readShortRequest(header
);
112 if (msg
.isInternal()) {
113 handleInternalMessage(msg
);
122 * @see IProtocol#writeRequest
124 public boolean writeRequest(
125 String oid
, TypeDescription type
, String function
, ThreadId tid
,
129 if (oid
.equals(PROPERTIES_OID
)) {
130 throw new IllegalArgumentException("illegal OID " + oid
);
132 synchronized (monitor
) {
133 while (!initialized
) {
136 } catch (InterruptedException e
) {
137 Thread
.currentThread().interrupt();
138 throw new RuntimeException(e
);
141 if (state
== STATE_TERMINATED
) {
142 throw new DisposedException();
144 return writeRequest(false, oid
, type
, function
, tid
, arguments
);
150 * @see IProtocol#writeReply
152 public void writeReply(boolean exception
, ThreadId tid
, Object result
)
155 synchronized (output
) {
156 writeQueuedReleases();
157 int header
= HEADER_LONGHEADER
;
158 PendingRequests
.Item pending
= pendingIn
.pop(tid
);
159 TypeDescription resultType
;
160 TypeDescription
[] argTypes
;
163 header
|= HEADER_EXCEPTION
;
164 resultType
= TypeDescription
.getTypeDescription(TypeClass
.ANY
);
168 resultType
= pending
.function
.getReturnSignature();
169 argTypes
= pending
.function
.getOutSignature();
170 args
= pending
.arguments
;
172 if (!tid
.equals(outL1Tid
)) {
173 header
|= HEADER_NEWTID
;
178 marshal
.write8Bit(header
);
180 marshal
.writeThreadId(tid
);
182 marshal
.writeValue(resultType
, result
);
183 if (argTypes
!= null) {
184 for (int i
= 0; i
< argTypes
.length
; ++i
) {
185 if (argTypes
[i
] != null) {
187 argTypes
[i
].getComponentType(),
188 Array
.get(args
[i
], 0));
196 private void sendRequestChange() throws IOException
{
197 if (propertiesTid
== null) {
198 propertiesTid
= ThreadId
.createFresh();
200 random
= randomGenerator
.nextInt();
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
;
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"
217 int fid
= message
.getMethod().getIndex();
219 case PROPERTIES_FID_REQUEST_CHANGE
:
220 checkSynchronousPropertyRequest(message
);
221 synchronized (monitor
) {
226 false, message
.getThreadId(), Integer
.valueOf(1));
229 case STATE_REQUESTED
:
231 = ((Integer
) message
.getArguments()[0]).intValue();
234 false, message
.getThreadId(), Integer
.valueOf(1));
236 } else if (random
== n
) {
238 false, message
.getThreadId(), Integer
.valueOf(-1));
239 state
= STATE_INITIAL
;
243 false, message
.getThreadId(), Integer
.valueOf(0));
248 true, message
.getThreadId(),
249 new com
.sun
.star
.uno
.RuntimeException(
250 "read URP protocol properties requestChange"
251 + " request in illegal state"));
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];
265 for (; i
< p
.length
; ++i
) {
266 if (p
[i
].Name
.equals(PROPERTY_CURRENT_CONTEXT
)) {
274 writeReply(false, message
.getThreadId(), null);
277 true, message
.getThreadId(),
278 new InvalidProtocolChangeException(
281 state
= STATE_INITIAL
;
284 currentContext
= true;
293 true, message
.getThreadId(),
294 new com
.sun
.star
.uno
.RuntimeException(
295 "read URP protocol properties commitChange"
296 + " request in illegal state"));
301 throw new IOException(
302 "read URP protocol properties request with unsupported"
303 + " function ID " + fid
);
306 synchronized (monitor
) {
307 if (state
== STATE_COMMITTED
) {
308 // commitChange reply:
309 if (!message
.isAbnormalTermination()) {
310 currentContext
= true;
312 state
= STATE_INITIAL
;
316 // requestChange reply:
317 if (message
.isAbnormalTermination()) {
318 // remote side probably does not support negotiation:
319 state
= STATE_INITIAL
;
323 int n
= ((Integer
) message
.getResult()).intValue();
330 true, PROPERTIES_OID
,
331 TypeDescription
.getTypeDescription(
332 XProtocolProperties
.class),
333 PROPERTIES_FUN_COMMIT_CHANGE
, propertiesTid
,
335 new ProtocolProperty
[] {
336 new ProtocolProperty(
337 PROPERTY_CURRENT_CONTEXT
,
339 state
= STATE_COMMITTED
;
342 throw new IOException(
343 "read URP protocol properties "
344 + PROPERTIES_FUN_REQUEST_CHANGE
345 + " reply with illegal return value " + n
);
353 private void checkSynchronousPropertyRequest(Message message
)
356 if (!message
.isSynchronous()) {
357 throw new IOException(
358 "read URP protocol properties request for synchronous function"
359 + " marked as not SYNCHRONOUS");
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
);
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
))
376 throw new IOException(
377 "read URP request with bad MUSTREPLY/SYNCHRONOUS byte");
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
);
390 if ((header
& HEADER_NEWOID
) != 0) {
391 inL1Oid
= unmarshal
.readObjectId();
393 if ((header
& HEADER_NEWTID
) != 0) {
394 inL1Tid
= unmarshal
.readThreadId();
396 return readRequest(funId
, sync
);
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);
406 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 MethodDescription desc
= inL1Type
.getMethodDescription(functionId
);
419 throw new IOException(
420 "read URP request with unsupported function ID " + functionId
);
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);
433 outSig
[i
].getComponentType()));
436 args
[i
] = unmarshal
.readValue(inSig
[i
]);
439 args
[i
] = Array
.newInstance(
440 outSig
[i
].getComponentType().getZClass(), 1);
443 boolean sync
= forcedSynchronous
|| !desc
.isOneway();
446 inL1Tid
, new PendingRequests
.Item(internal
, desc
, args
));
448 return new UrpMessage(
449 inL1Tid
, true, inL1Oid
, inL1Type
, desc
, sync
, cc
, false, null, args
,
453 private UrpMessage
readReply(int header
) {
454 if ((header
& HEADER_NEWTID
) != 0) {
455 inL1Tid
= unmarshal
.readThreadId();
457 PendingRequests
.Item pending
= pendingOut
.pop(inL1Tid
);
458 TypeDescription resultType
;
459 TypeDescription
[] argTypes
;
461 boolean exception
= (header
& HEADER_EXCEPTION
) != 0;
463 resultType
= TypeDescription
.getTypeDescription(TypeClass
.ANY
);
467 resultType
= pending
.function
.getReturnSignature();
468 argTypes
= pending
.function
.getOutSignature();
469 args
= pending
.arguments
;
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) {
479 argTypes
[i
].getComponentType()));
483 return new UrpMessage(
484 inL1Tid
, false, null, null, null, false, null, exception
, result
,
485 args
, pending
.internal
);
488 private boolean writeRequest(
489 boolean internal
, String oid
, TypeDescription type
, String function
,
490 ThreadId tid
, Object
[] arguments
)
493 MethodDescription desc
= type
.getMethodDescription(function
);
494 synchronized (output
) {
495 if (desc
.getIndex() == MethodDescription
.ID_RELEASE
496 && releaseQueue
.size() < MAX_RELEASE_QUEUE_SIZE
)
499 new QueuedRelease(internal
, oid
, type
, desc
, tid
));
502 writeQueuedReleases();
504 internal
, oid
, type
, desc
, tid
, arguments
, true);
509 private boolean writeRequest(
510 boolean internal
, String oid
, TypeDescription type
,
511 MethodDescription desc
, ThreadId tid
, Object
[] arguments
,
515 int funId
= desc
.getIndex();
516 if (funId
< 0 || funId
> MAX_FUNCTIONID16
) {
517 throw new IllegalArgumentException(
518 "function ID " + funId
+ " out of range");
520 boolean forceSync
= forceSynchronous
521 && funId
!= MethodDescription
.ID_RELEASE
;
522 boolean moreFlags
= forceSync
&& desc
.isOneway();
523 boolean longHeader
= moreFlags
;
525 if (!type
.equals(outL1Type
)) {
527 header
|= HEADER_NEWTYPE
;
532 if (!oid
.equals(outL1Oid
)) {
534 header
|= HEADER_NEWOID
;
539 if (!tid
.equals(outL1Tid
)) {
541 header
|= HEADER_NEWTID
;
546 if (funId
> MAX_FUNCTIONID14
) {
550 header
|= HEADER_LONGHEADER
| HEADER_REQUEST
;
551 if (funId
> MAX_FUNCTIONID8
) {
552 header
|= HEADER_FUNCTIONID16
;
555 header
|= HEADER_MOREFLAGS
;
557 marshal
.write8Bit(header
);
559 marshal
.write8Bit(HEADER_MUSTREPLY
| HEADER_SYNCHRONOUS
);
561 if (funId
> MAX_FUNCTIONID8
) {
562 marshal
.write16Bit(funId
);
564 marshal
.write8Bit(funId
);
567 marshal
.writeType(type
);
570 marshal
.writeObjectId(oid
);
573 marshal
.writeThreadId(tid
);
576 if (funId
> HEADER_FUNCTIONID
) {
577 marshal
.write8Bit(HEADER_FUNCTIONID14
| (funId
>> 8));
579 marshal
.write8Bit(funId
);
581 if (currentContext
&& !internal
582 && funId
!= MethodDescription
.ID_RELEASE
)
584 marshal
.writeInterface(
585 UnoRuntime
.getCurrentContext(),
586 new Type(XCurrentContext
.class));
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) {
594 outSig
[i
].getComponentType(),
595 ((Object
[]) arguments
[i
])[0]);
598 inSig
[i
], arguments
[i
]);
602 boolean sync
= forceSync
|| !desc
.isOneway();
605 outL1Tid
, new PendingRequests
.Item(internal
, desc
, arguments
));
611 private void writeBlock(boolean flush
) throws IOException
{
612 byte[] data
= marshal
.reset();
613 output
.writeInt(data
.length
);
621 private void writeQueuedReleases() throws IOException
{
622 for (int i
= releaseQueue
.size(); i
> 0;) {
624 QueuedRelease r
= releaseQueue
.get(i
);
626 r
.internal
, r
.objectId
, r
.type
, r
.method
, r
.threadId
, null,
628 releaseQueue
.remove(i
);
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();
639 int i
= a
.indexOf('=');
641 v
= a
.substring(i
+ 1);
642 a
= a
.substring(0, i
);
644 if (a
.equalsIgnoreCase("ForceSynchronous")) {
645 forceSynchronous
= parseBooleanAttributeValue(a
, v
);
646 } else if (a
.equalsIgnoreCase("negotiate")) {
648 parseBooleanAttributeValue(a
, v
);
650 throw new IllegalArgumentException(
651 "unknown protocol attribute " + a
);
655 return forceSynchronous
;
658 private static boolean parseBooleanAttributeValue(
659 String attribute
, String value
)
662 throw new IllegalArgumentException(
663 "missing value for protocol attribute " + attribute
);
665 if (value
.equals("0")) {
667 } else if (value
.equals("1")) {
670 throw new IllegalArgumentException(
671 "bad value " + value
+ " for protocol attribute " + attribute
);
675 private static final class QueuedRelease
{
676 public QueuedRelease(
677 boolean internal
, String objectId
, TypeDescription type
,
678 MethodDescription method
, ThreadId threadId
)
680 this.internal
= internal
;
681 this.objectId
= objectId
;
683 this.method
= method
;
684 this.threadId
= threadId
;
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
;
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";
701 private static final short CACHE_SIZE
= 256;
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;
716 private static final int MAX_FUNCTIONID16
= 0xFFFF;
717 private static final int MAX_FUNCTIONID14
= 0x3FFF;
718 private static final int MAX_FUNCTIONID8
= 0xFF;
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;
727 private static final int MAX_RELEASE_QUEUE_SIZE
= 100;
729 private static final Random randomGenerator
= new Random();
731 private final DataInput input
;
732 private final DataOutputStream output
;
734 private final Marshal marshal
;
735 private final Unmarshal unmarshal
;
737 private final boolean forceSynchronous
;
739 private final PendingRequests pendingIn
= new PendingRequests();
740 private final PendingRequests pendingOut
= new PendingRequests();
742 private final Object monitor
= new Object();
743 private int state
= STATE_INITIAL0
;
744 private boolean initialized
= false;
745 private ThreadId propertiesTid
= null;
747 private boolean currentContext
= false;
749 private ThreadId inL1Tid
= null;
750 private String inL1Oid
= null;
751 private TypeDescription inL1Type
= null;
753 private ThreadId outL1Tid
= null;
754 private String outL1Oid
= null;
755 private TypeDescription outL1Type
= null;
757 private final ArrayList
<QueuedRelease
> releaseQueue
= new ArrayList
<QueuedRelease
>(); // of QueuedRelease
760 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */