bump product version to 5.0.4.1
[LibreOffice.git] / jurt / com / sun / star / lib / uno / protocols / urp / urp.java
blobb527e644c5a200d860a7c5fa8740ce42d83cbf56
1 /*
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;
49 /**
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 {
55 public urp(
56 IBridge bridge, String attributes, InputStream input,
57 OutputStream output)
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);
66 /**
68 * @see IProtocol#init
70 public void init() throws IOException {
71 synchronized (monitor) {
72 if (state == STATE_INITIAL0) {
73 sendRequestChange();
78 /**
80 * @see IProtocol#terminate
82 public void terminate() {
83 synchronized (monitor) {
84 state = STATE_TERMINATED;
85 initialized = true;
86 monitor.notifyAll();
90 /**
92 * @see IProtocol#readMessage
94 public Message readMessage() throws IOException {
95 for (;;) {
96 if (!unmarshal.hasMore()) {
97 unmarshal.reset(readBlock());
98 if (!unmarshal.hasMore()) {
99 throw new IOException("closeConnection message received");
102 UrpMessage msg;
103 int header = unmarshal.read8Bit();
104 if ((header & HEADER_LONGHEADER) != 0) {
105 if ((header & HEADER_REQUEST) != 0) {
106 msg = readLongRequest(header);
107 } else {
108 msg = readReply(header);
110 } else {
111 msg = readShortRequest(header);
113 if (msg.isInternal()) {
114 handleInternalMessage(msg);
115 } else {
116 return msg;
123 * @see IProtocol#writeRequest
125 public boolean writeRequest(
126 String oid, TypeDescription type, String function, ThreadId tid,
127 Object[] arguments)
128 throws IOException
130 if (oid.equals(PROPERTIES_OID)) {
131 throw new IllegalArgumentException("illegal OID " + oid);
133 synchronized (monitor) {
134 while (!initialized) {
135 try {
136 monitor.wait();
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)
154 throws IOException
156 synchronized (output) {
157 writeQueuedReleases();
158 int header = HEADER_LONGHEADER;
159 PendingRequests.Item pending = pendingIn.pop(tid);
160 TypeDescription resultType;
161 ITypeDescription[] argTypes;
162 Object[] args;
163 if (exception) {
164 header |= HEADER_EXCEPTION;
165 resultType = TypeDescription.getTypeDescription(TypeClass.ANY);
166 argTypes = null;
167 args = null;
168 } else {
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;
176 outL1Tid = tid;
177 } else {
178 tid = null;
180 marshal.write8Bit(header);
181 if (tid != null) {
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) {
188 marshal.writeValue(
189 (TypeDescription) argTypes[i].getComponentType(),
190 Array.get(args[i], 0));
194 writeBlock(true);
198 private void sendRequestChange() throws IOException {
199 if (propertiesTid == null) {
200 propertiesTid = ThreadId.createFresh();
202 random = new Random().nextInt();
203 writeRequest(
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"
217 + " type " + t);
219 int fid = message.getMethod().getIndex();
220 switch (fid) {
221 case PROPERTIES_FID_REQUEST_CHANGE:
222 checkSynchronousPropertyRequest(message);
223 synchronized (monitor) {
224 switch (state) {
225 case STATE_INITIAL0:
226 case STATE_INITIAL:
227 writeReply(
228 false, message.getThreadId(), Integer.valueOf(1));
229 state = STATE_WAIT;
230 break;
231 case STATE_REQUESTED:
232 int n
233 = ((Integer) message.getArguments()[0]).intValue();
234 if (random < n) {
235 writeReply(
236 false, message.getThreadId(), Integer.valueOf(1));
237 state = STATE_WAIT;
238 } else if (random == n) {
239 writeReply(
240 false, message.getThreadId(), Integer.valueOf(-1));
241 state = STATE_INITIAL;
242 sendRequestChange();
243 } else {
244 writeReply(
245 false, message.getThreadId(), Integer.valueOf(0));
247 break;
248 default:
249 writeReply(
250 true, message.getThreadId(),
251 new com.sun.star.uno.RuntimeException(
252 "read URP protocol properties requestChange"
253 + " request in illegal state"));
254 break;
257 break;
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];
264 boolean ok = true;
265 boolean cc = false;
266 int i = 0;
267 for (; i < p.length; ++i) {
268 if (p[i].Name.equals(PROPERTY_CURRENT_CONTEXT)) {
269 cc = true;
270 } else {
271 ok = false;
272 break;
275 if (ok) {
276 writeReply(false, message.getThreadId(), null);
277 } else {
278 writeReply(
279 true, message.getThreadId(),
280 new InvalidProtocolChangeException(
281 "", null, p[i], 1));
283 state = STATE_INITIAL;
284 if (!initialized) {
285 if (cc) {
286 currentContext = true;
287 initialized = true;
288 monitor.notifyAll();
289 } else {
290 sendRequestChange();
293 } else {
294 writeReply(
295 true, message.getThreadId(),
296 new com.sun.star.uno.RuntimeException(
297 "read URP protocol properties commitChange"
298 + " request in illegal state"));
301 break;
302 default:
303 throw new IOException(
304 "read URP protocol properties request with unsupported"
305 + " function ID " + fid);
307 } else {
308 synchronized (monitor) {
309 if (state == STATE_COMMITTED) {
310 // commitChange reply:
311 if (!message.isAbnormalTermination()) {
312 currentContext = true;
314 state = STATE_INITIAL;
315 initialized = true;
316 monitor.notifyAll();
317 } else {
318 // requestChange reply:
319 if (message.isAbnormalTermination()) {
320 // remote side probably does not support negotiation:
321 state = STATE_INITIAL;
322 initialized = true;
323 monitor.notifyAll();
324 } else {
325 int n = ((Integer) message.getResult()).intValue();
326 switch (n) {
327 case -1:
328 case 0:
329 break;
330 case 1:
331 writeRequest(
332 true, PROPERTIES_OID,
333 TypeDescription.getTypeDescription(
334 XProtocolProperties.class),
335 PROPERTIES_FUN_COMMIT_CHANGE, propertiesTid,
336 new Object[] {
337 new ProtocolProperty[] {
338 new ProtocolProperty(
339 PROPERTY_CURRENT_CONTEXT,
340 Any.VOID) } });
341 state = STATE_COMMITTED;
342 break;
343 default:
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)
356 throws IOException
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);
370 return 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");
381 sync = true;
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("")
411 XCurrentContext cc =
412 (currentContext && !internal
413 && functionId != MethodDescription.ID_RELEASE)
414 ? (XCurrentContext) unmarshal.readInterface(
415 new Type(XCurrentContext.class))
416 : null;
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);
426 Array.set(
427 inout, 0,
428 unmarshal.readValue(
429 (TypeDescription) outSig[i].getComponentType()));
430 args[i] = inout;
431 } else {
432 args[i] = unmarshal.readValue((TypeDescription) inSig[i]);
434 } else {
435 args[i] = Array.newInstance(
436 outSig[i].getComponentType().getZClass(), 1);
439 boolean sync = forcedSynchronous || !desc.isOneway();
440 if (sync) {
441 pendingIn.push(
442 inL1Tid, new PendingRequests.Item(internal, desc, args));
444 return new UrpMessage(
445 inL1Tid, true, inL1Oid, inL1Type, desc, sync, cc, false, null, args,
446 internal);
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;
456 Object[] args;
457 boolean exception = (header & HEADER_EXCEPTION) != 0;
458 if (exception) {
459 resultType = TypeDescription.getTypeDescription(TypeClass.ANY);
460 argTypes = null;
461 args = null;
462 } else {
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) {
473 Array.set(
474 args[i], 0,
475 unmarshal.readValue(
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)
488 throws IOException
490 IMethodDescription desc = type.getMethodDescription(function);
491 synchronized (output) {
492 if (desc.getIndex() == MethodDescription.ID_RELEASE
493 && releaseQueue.size() < MAX_RELEASE_QUEUE_SIZE)
495 releaseQueue.add(
496 new QueuedRelease(internal, oid, type, desc, tid));
497 return false;
498 } else {
499 writeQueuedReleases();
500 return writeRequest(
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,
509 boolean flush)
510 throws IOException
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;
521 int header = 0;
522 if (!type.equals(outL1Type)) {
523 longHeader = true;
524 header |= HEADER_NEWTYPE;
525 outL1Type = type;
526 } else {
527 type = null;
529 if (!oid.equals(outL1Oid)) {
530 longHeader = true;
531 header |= HEADER_NEWOID;
532 outL1Oid = oid;
533 } else {
534 oid = null;
536 if (!tid.equals(outL1Tid)) {
537 longHeader = true;
538 header |= HEADER_NEWTID;
539 outL1Tid = tid;
540 } else {
541 tid = null;
543 if (funId > MAX_FUNCTIONID14) {
544 longHeader = true;
546 if (longHeader) {
547 header |= HEADER_LONGHEADER | HEADER_REQUEST;
548 if (funId > MAX_FUNCTIONID8) {
549 header |= HEADER_FUNCTIONID16;
551 if (moreFlags) {
552 header |= HEADER_MOREFLAGS;
554 marshal.write8Bit(header);
555 if (moreFlags) {
556 marshal.write8Bit(HEADER_MUSTREPLY | HEADER_SYNCHRONOUS);
558 if (funId > MAX_FUNCTIONID8) {
559 marshal.write16Bit(funId);
560 } else {
561 marshal.write8Bit(funId);
563 if (type != null) {
564 marshal.writeType(type);
566 if (oid != null) {
567 marshal.writeObjectId(oid);
569 if (tid != null) {
570 marshal.writeThreadId(tid);
572 } else {
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) {
590 marshal.writeValue(
591 (TypeDescription) outSig[i].getComponentType(),
592 ((Object[]) arguments[i])[0]);
593 } else {
594 marshal.writeValue(
595 (TypeDescription) inSig[i], arguments[i]);
599 boolean sync = forceSync || !desc.isOneway();
600 if (sync) {
601 pendingOut.push(
602 outL1Tid, new PendingRequests.Item(internal, desc, arguments));
604 writeBlock(flush);
605 return sync;
608 private void writeBlock(boolean flush) throws IOException {
609 byte[] data = marshal.reset();
610 output.writeInt(data.length);
611 output.writeInt(1);
612 output.write(data);
613 if (flush) {
614 output.flush();
618 private void writeQueuedReleases() throws IOException {
619 for (int i = releaseQueue.size(); i > 0;) {
620 --i;
621 QueuedRelease r = releaseQueue.get(i);
622 writeRequest(
623 r.internal, r.objectId, r.type, r.method, r.threadId, null,
624 false);
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();
635 String v = null;
636 int i = a.indexOf('=');
637 if (i >= 0) {
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")) {
644 // Ignored:
645 parseBooleanAttributeValue(a, v);
646 } else {
647 throw new IllegalArgumentException(
648 "unknown protocol attribute " + a);
652 return forceSynchronous;
655 private static boolean parseBooleanAttributeValue(
656 String attribute, String value)
658 if (value == null) {
659 throw new IllegalArgumentException(
660 "missing value for protocol attribute " + attribute);
662 if (value.equals("0")) {
663 return false;
664 } else if (value.equals("1")) {
665 return true;
666 } else {
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;
679 this.type = type;
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;
741 private int random;
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