bump product version to 6.4.0.3
[LibreOffice.git] / jurt / com / sun / star / lib / uno / protocols / urp / urp.java
blob0ce9d35be75fd65eaf07f290a1a2d822ddb6bf07
1 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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;
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.
53 public final class urp implements IProtocol {
54 public urp(
55 IBridge bridge, String attributes, InputStream input,
56 OutputStream output)
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);
65 /**
67 * @see IProtocol#init
69 public void init() throws IOException {
70 synchronized (monitor) {
71 if (state == STATE_INITIAL0) {
72 sendRequestChange();
77 /**
79 * @see IProtocol#terminate
81 public void terminate() {
82 synchronized (monitor) {
83 state = STATE_TERMINATED;
84 initialized = true;
85 monitor.notifyAll();
89 /**
91 * @see IProtocol#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");
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);
109 } else {
110 msg = readShortRequest(header);
112 if (msg.isInternal()) {
113 handleInternalMessage(msg);
114 } else {
115 return msg;
122 * @see IProtocol#writeRequest
124 public boolean writeRequest(
125 String oid, TypeDescription type, String function, ThreadId tid,
126 Object[] arguments)
127 throws IOException
129 if (oid.equals(PROPERTIES_OID)) {
130 throw new IllegalArgumentException("illegal OID " + oid);
132 synchronized (monitor) {
133 while (!initialized) {
134 try {
135 monitor.wait();
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)
153 throws IOException
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;
172 if (!tid.equals(outL1Tid)) {
173 header |= HEADER_NEWTID;
174 outL1Tid = tid;
175 } else {
176 tid = null;
178 marshal.write8Bit(header);
179 if (tid != null) {
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) {
186 marshal.writeValue(
187 argTypes[i].getComponentType(),
188 Array.get(args[i], 0));
192 writeBlock(true);
196 private void sendRequestChange() throws IOException {
197 if (propertiesTid == null) {
198 propertiesTid = ThreadId.createFresh();
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;
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);
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));
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;
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;
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));
281 state = STATE_INITIAL;
282 if (!initialized) {
283 if (cc) {
284 currentContext = true;
285 initialized = true;
286 monitor.notifyAll();
287 } else {
288 sendRequestChange();
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"));
299 break;
300 default:
301 throw new IOException(
302 "read URP protocol properties request with unsupported"
303 + " function ID " + fid);
305 } else {
306 synchronized (monitor) {
307 if (state == STATE_COMMITTED) {
308 // commitChange reply:
309 if (!message.isAbnormalTermination()) {
310 currentContext = true;
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);
353 private void checkSynchronousPropertyRequest(Message message)
354 throws IOException
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);
368 return 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");
379 sync = true;
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)
407 throws IOException
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);
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]);
438 } else {
439 args[i] = Array.newInstance(
440 outSig[i].getComponentType().getZClass(), 1);
443 boolean sync = forcedSynchronous || !desc.isOneway();
444 if (sync) {
445 pendingIn.push(
446 inL1Tid, new PendingRequests.Item(internal, desc, args));
448 return new UrpMessage(
449 inL1Tid, true, inL1Oid, inL1Type, desc, sync, cc, false, null, args,
450 internal);
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;
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;
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()));
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)
491 throws IOException
493 MethodDescription desc = type.getMethodDescription(function);
494 synchronized (output) {
495 if (desc.getIndex() == MethodDescription.ID_RELEASE
496 && releaseQueue.size() < MAX_RELEASE_QUEUE_SIZE)
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);
509 private boolean writeRequest(
510 boolean internal, String oid, TypeDescription type,
511 MethodDescription desc, ThreadId tid, Object[] arguments,
512 boolean flush)
513 throws IOException
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;
524 int header = 0;
525 if (!type.equals(outL1Type)) {
526 longHeader = true;
527 header |= HEADER_NEWTYPE;
528 outL1Type = type;
529 } else {
530 type = null;
532 if (!oid.equals(outL1Oid)) {
533 longHeader = true;
534 header |= HEADER_NEWOID;
535 outL1Oid = oid;
536 } else {
537 oid = null;
539 if (!tid.equals(outL1Tid)) {
540 longHeader = true;
541 header |= HEADER_NEWTID;
542 outL1Tid = tid;
543 } else {
544 tid = null;
546 if (funId > MAX_FUNCTIONID14) {
547 longHeader = true;
549 if (longHeader) {
550 header |= HEADER_LONGHEADER | HEADER_REQUEST;
551 if (funId > MAX_FUNCTIONID8) {
552 header |= HEADER_FUNCTIONID16;
554 if (moreFlags) {
555 header |= HEADER_MOREFLAGS;
557 marshal.write8Bit(header);
558 if (moreFlags) {
559 marshal.write8Bit(HEADER_MUSTREPLY | HEADER_SYNCHRONOUS);
561 if (funId > MAX_FUNCTIONID8) {
562 marshal.write16Bit(funId);
563 } else {
564 marshal.write8Bit(funId);
566 if (type != null) {
567 marshal.writeType(type);
569 if (oid != null) {
570 marshal.writeObjectId(oid);
572 if (tid != null) {
573 marshal.writeThreadId(tid);
575 } else {
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) {
593 marshal.writeValue(
594 outSig[i].getComponentType(),
595 ((Object[]) arguments[i])[0]);
596 } else {
597 marshal.writeValue(
598 inSig[i], arguments[i]);
602 boolean sync = forceSync || !desc.isOneway();
603 if (sync) {
604 pendingOut.push(
605 outL1Tid, new PendingRequests.Item(internal, desc, arguments));
607 writeBlock(flush);
608 return sync;
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();
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);
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);
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);
655 return forceSynchronous;
658 private static boolean parseBooleanAttributeValue(
659 String attribute, String value)
661 if (value == null) {
662 throw new IllegalArgumentException(
663 "missing value for protocol attribute " + attribute);
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);
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;
682 this.type = type;
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;
746 private int random;
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: */