summaryrefslogtreecommitdiffstats
path: root/ridljar/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java
diff options
context:
space:
mode:
Diffstat (limited to 'ridljar/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java')
-rw-r--r--ridljar/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java700
1 files changed, 700 insertions, 0 deletions
diff --git a/ridljar/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java b/ridljar/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java
new file mode 100644
index 000000000..392e031e3
--- /dev/null
+++ b/ridljar/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java
@@ -0,0 +1,700 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.lib.uno.bridges.java_remote;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.sun.star.bridge.XBridge;
+import com.sun.star.bridge.XInstanceProvider;
+import com.sun.star.connection.XConnection;
+import com.sun.star.lang.DisposedException;
+import com.sun.star.lang.EventObject;
+import com.sun.star.lang.XComponent;
+import com.sun.star.lang.XEventListener;
+import com.sun.star.lib.uno.environments.java.java_environment;
+import com.sun.star.lib.uno.environments.remote.IProtocol;
+import com.sun.star.lib.uno.environments.remote.IReceiver;
+import com.sun.star.lib.uno.environments.remote.IThreadPool;
+import com.sun.star.lib.uno.environments.remote.Job;
+import com.sun.star.lib.uno.environments.remote.Message;
+import com.sun.star.lib.uno.environments.remote.ThreadId;
+import com.sun.star.lib.uno.environments.remote.ThreadPoolManager;
+import com.sun.star.lib.uno.typedesc.MethodDescription;
+import com.sun.star.lib.uno.typedesc.TypeDescription;
+import com.sun.star.lib.util.DisposeListener;
+import com.sun.star.lib.util.DisposeNotifier;
+import com.sun.star.uno.Any;
+import com.sun.star.uno.IBridge;
+import com.sun.star.uno.IEnvironment;
+import com.sun.star.uno.Type;
+import com.sun.star.uno.TypeClass;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+/**
+ * This class implements a remote bridge.
+ *
+ * <p>Therefore various interfaces are implemented.</p>
+ *
+ * <p>The protocol to used is passed by name, the bridge
+ * then looks for it under <code>com.sun.star.lib.uno.protocols</code>.</p>
+ *
+ * @since UDK1.0
+ */
+public class java_remote_bridge
+ implements IBridge, IReceiver, RequestHandler, XBridge, XComponent,
+ DisposeNotifier
+{
+ /**
+ * When set to true, enables various debugging output.
+ */
+ private static final boolean DEBUG = false;
+
+ private final class MessageDispatcher extends Thread {
+ public MessageDispatcher() {
+ super("MessageDispatcher");
+ }
+
+ @Override
+ public void run() {
+ try {
+ for (;;) {
+ synchronized (this) {
+ if (terminate) {
+ break;
+ }
+ }
+ Message msg = _iProtocol.readMessage();
+ Object obj = null;
+ if (msg.isRequest()) {
+ String oid = msg.getObjectId();
+ Type type = new Type(msg.getType());
+ int fid = msg.getMethod().getIndex();
+ if (fid == MethodDescription.ID_RELEASE) {
+ _java_environment.revokeInterface(oid, type);
+ remRefHolder(type, oid);
+ if (msg.isSynchronous()) {
+ sendReply(false, msg.getThreadId(), null);
+ }
+ continue;
+ }
+ obj = _java_environment.getRegisteredInterface(
+ oid, type);
+ if (obj == null
+ && fid == MethodDescription.ID_QUERY_INTERFACE)
+ {
+ if (_xInstanceProvider == null) {
+ sendReply(
+ true, msg.getThreadId(),
+ new com.sun.star.uno.RuntimeException(
+ "unknown OID " + oid));
+ continue;
+ } else {
+ UnoRuntime.setCurrentContext(
+ msg.getCurrentContext());
+ try {
+ obj = _xInstanceProvider.getInstance(oid);
+ } catch (com.sun.star.uno.RuntimeException e) {
+ sendReply(true, msg.getThreadId(), e);
+ continue;
+ } catch (Exception e) {
+ sendReply(
+ true, msg.getThreadId(),
+ new com.sun.star.uno.RuntimeException(
+ e.toString()));
+ continue;
+ } finally {
+ UnoRuntime.setCurrentContext(null);
+ }
+ }
+ }
+ }
+ _iThreadPool.putJob(
+ new Job(obj, java_remote_bridge.this, msg));
+ }
+ } catch (Throwable e) {
+ dispose(e);
+ }
+ }
+
+ public synchronized void terminate() {
+ terminate = true;
+ }
+
+ private boolean terminate = false;
+ }
+
+ protected XConnection _xConnection;
+
+ protected XInstanceProvider _xInstanceProvider;
+
+ protected String _name = "remote";
+ private final String protocol;
+ protected IProtocol _iProtocol;
+ protected IEnvironment _java_environment;
+ protected MessageDispatcher _messageDispatcher;
+ protected final AtomicInteger _life_count = new AtomicInteger(); // determines if this bridge is alive, which is controlled by acquire and release calls
+
+ private final ArrayList<XEventListener> _listeners = new ArrayList<XEventListener>();
+
+ protected IThreadPool _iThreadPool;
+
+ // Variable disposed must only be used while synchronized on this object:
+ private boolean disposed = false;
+
+ /**
+ * This method is for testing only.
+ */
+ int getLifeCount() {
+ return _life_count.get();
+ }
+
+ /**
+ * This method is for testing only.
+ */
+ IProtocol getProtocol() {
+ return _iProtocol;
+ }
+
+ /**
+ * The ref holder stuff strongly holds objects mapped out via this bridge
+ * (the java_environment only holds them weakly).
+ *
+ * <p>When this bridge is disposed, all remaining ref holder entries are
+ * released.</p>
+ */
+ private static final class RefHolder {
+ public RefHolder(Type type, Object object) {
+ this.type = type;
+ this.object = object;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public void acquire() {
+ ++count;
+ }
+
+ public boolean release() {
+ return --count == 0;
+ }
+
+ private final Type type;
+ @SuppressWarnings("unused")
+ private final Object object;
+ private int count = 1;
+ }
+
+ private final HashMap<String, LinkedList<RefHolder>> refHolders = new HashMap<String, LinkedList<RefHolder>>();
+ // from OID (String) to LinkedList of RefHolder
+
+ private boolean hasRefHolder(String oid, Type type) {
+ synchronized (refHolders) {
+ LinkedList<RefHolder> l = refHolders.get(oid);
+ if (l != null) {
+ for (RefHolder rh : l) {
+ if (type.isSupertypeOf(rh.getType())) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ final void addRefHolder(Object obj, Type type, String oid) {
+ synchronized (refHolders) {
+ LinkedList<RefHolder> l = refHolders.get(oid);
+ if (l == null) {
+ l = new LinkedList<RefHolder>();
+ refHolders.put(oid, l);
+ }
+ boolean found = false;
+ for (Iterator<RefHolder> i = l.iterator(); !found && i.hasNext();) {
+ RefHolder rh = i.next();
+ if (rh.getType().equals(type)) {
+ found = true;
+ rh.acquire();
+ }
+ }
+ if (!found) {
+ l.add(new RefHolder(type, obj));
+ }
+ }
+ acquire();
+ }
+
+ final void remRefHolder(Type type, String oid) {
+ synchronized (refHolders) {
+ LinkedList<RefHolder> l = refHolders.get(oid);
+ if (l == null) {
+ return;
+ }
+ for (RefHolder rh : l) {
+ if (rh.getType().equals(type)) {
+ try {
+ if (rh.release()) {
+ l.remove(rh);
+ if (l.isEmpty()) {
+ refHolders.remove(oid);
+ }
+ }
+ } finally {
+ release();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ final void freeHolders() {
+ synchronized (refHolders) {
+ for (Iterator<Map.Entry<String,LinkedList<RefHolder>>> i1 = refHolders.entrySet().iterator(); i1.hasNext();)
+ {
+ Map.Entry<String,LinkedList<RefHolder>> e = i1.next();
+ String oid = e.getKey();
+ LinkedList<RefHolder> l = e.getValue();
+ for (Iterator<RefHolder> i2 = l.iterator(); i2.hasNext();) {
+ RefHolder rh = i2.next();
+ for (boolean done = false; !done;) {
+ done = rh.release();
+ _java_environment.revokeInterface(oid, rh.getType());
+ release();
+ }
+ }
+ }
+ refHolders.clear();
+ }
+ }
+
+ public java_remote_bridge(
+ IEnvironment java_environment, IEnvironment remote_environment,
+ Object[] args)
+ throws Exception
+ {
+ _java_environment = java_environment;
+ String proto = (String) args[0];
+ _xConnection = (XConnection) args[1];
+ _xInstanceProvider = (XInstanceProvider) args[2];
+ if (args.length > 3) {
+ _name = (String) args[3];
+ }
+ String attr;
+ int i = proto.indexOf(',');
+ if (i >= 0) {
+ protocol = proto.substring(0, i);
+ attr = proto.substring(i + 1);
+ } else {
+ protocol = proto;
+ attr = null;
+ }
+ _iProtocol = (IProtocol) Class.forName(
+ "com.sun.star.lib.uno.protocols." + protocol + "." + protocol).
+ getConstructor(
+ new Class[] {
+ IBridge.class, String.class, InputStream.class,
+ OutputStream.class }).
+ newInstance(
+ new Object[] {
+ this, attr,
+ new XConnectionInputStream_Adapter(_xConnection),
+ new XConnectionOutputStream_Adapter(_xConnection) });
+ proxyFactory = new ProxyFactory(this, this);
+ _iThreadPool = ThreadPoolManager.create();
+ _messageDispatcher = new MessageDispatcher();
+ _messageDispatcher.start();
+ _iProtocol.init();
+ }
+
+ private void notifyListeners() {
+ EventObject eventObject = new EventObject(this);
+
+ Iterator<XEventListener> elements = _listeners.iterator();
+ while(elements.hasNext()) {
+ XEventListener xEventListener = elements.next();
+
+ try {
+ xEventListener.disposing(eventObject);
+ }
+ catch(com.sun.star.uno.RuntimeException runtimeException) {
+ // we are here not interested in any exceptions
+ }
+ }
+ }
+
+ /**
+ * Constructs a new bridge.
+ * <p> This method is not part of the provided <code>api</code>
+ * and should only be used by the UNO runtime.</p>
+ *
+ * @param args the custom parameters: arg[0] == protocol_name,
+ * arg[1] == xConnection, arg[2] == xInstanceProvider.
+ *
+ * @deprecated as of UDK 1.0
+ */
+ @Deprecated
+ public java_remote_bridge(Object args[]) throws Exception {
+ this(UnoRuntime.getEnvironment("java", null), UnoRuntime.getEnvironment("remote", null), args);
+ }
+
+ /**
+ *
+ * @see com.sun.star.uno.IBridge#mapInterfaceTo
+ */
+ public Object mapInterfaceTo(Object object, Type type) {
+ checkDisposed();
+ if (object == null) {
+ return null;
+ } else {
+ String[] oid = new String[1];
+ object = _java_environment.registerInterface(object, oid, type);
+ if (!proxyFactory.isProxy(object)) {
+ // This branch must be taken iff object either is no proxy at
+ // all or a proxy from some other bridge. There are objects
+ // that behave like objects for this bridge but that are not
+ // detected as such by proxyFactory.isProxy. The only known
+ // case of such objects is com.sun.star.comp.beans.Wrapper,
+ // which implements com.sun.star.lib.uno.Proxy and effectively
+ // is a second proxy around a proxy that can be from this
+ // bridge. For that case, there is no problem, however: Since
+ // the proxies generated by ProxyFactory send each
+ // queryInterface to the original object (i.e., they do not
+ // short-circuit requests for a super-interface to themselves),
+ // there will always be an appropriate ProxyFactory-proxy
+ // registered at the _java_environment, so that the object
+ // returned by _java_environment.registerInterface will never be
+ // a com.sun.star.comp.beans.Wrapper.
+ addRefHolder(object, type, oid[0]);
+ }
+ return oid[0];
+ }
+ }
+
+ /**
+ * Maps an object from destination environment to the source environment.
+ *
+ * @param oId the object to map.
+ * @param type the interface under which is to be mapped.
+ * @return the object in the source environment.
+ *
+ * @see com.sun.star.uno.IBridge#mapInterfaceFrom
+ */
+ public Object mapInterfaceFrom(Object oId, Type type) {
+ checkDisposed();
+ // TODO What happens if an exception is thrown after the call to
+ // acquire, but before it is guaranteed that a pairing release will be
+ // called eventually?
+ acquire();
+ String oid = (String) oId;
+ Object object = _java_environment.getRegisteredInterface(oid, type);
+ if (object == null) {
+ object = _java_environment.registerInterface(
+ proxyFactory.create(oid, type), new String[] { oid }, type);
+ // the proxy sends a release when finalized
+ } else if (!hasRefHolder(oid, type)) {
+ sendInternalRequest(oid, type, "release", null);
+ }
+ return object;
+ }
+
+ /**
+ * Gives the source environment.
+ *
+ * @return the source environment of this bridge.
+ * @see com.sun.star.uno.IBridge#getSourceEnvironment
+ */
+ public IEnvironment getSourceEnvironment() {
+ return _java_environment;
+ }
+
+ /**
+ * Gives the destination environment.
+ *
+ * @return the destination environment of this bridge.
+ * @see com.sun.star.uno.IBridge#getTargetEnvironment
+ */
+ public IEnvironment getTargetEnvironment() {
+ return null;
+ }
+
+ /**
+ * Increases the life count.
+ *
+ * @see com.sun.star.uno.IBridge#acquire
+ */
+ public void acquire() {
+ if(DEBUG) {
+ int x = _life_count.incrementAndGet();
+ System.err.println("##### " + getClass().getName() + ".acquire:" + x);
+ } else {
+ _life_count.incrementAndGet();
+ }
+ }
+
+ /**
+ * Decreases the life count.
+ *
+ * <p>If the life count drops to zero, the bridge disposes itself.</p>
+ *
+ * @see com.sun.star.uno.IBridge#release
+ */
+ public void release() {
+ int x = _life_count.decrementAndGet();
+ if (x <= 0) {
+ dispose(new Throwable("end of life"));
+ }
+ }
+
+ public void dispose() {
+ dispose(new Throwable("user dispose"));
+ }
+
+ private void dispose(Throwable throwable) {
+ synchronized (this) {
+ if (disposed) {
+ return;
+ }
+ disposed = true;
+ }
+
+ notifyListeners();
+ for (Iterator<DisposeListener> i = disposeListeners.iterator(); i.hasNext();) {
+ i.next().notifyDispose(this);
+ }
+
+ _iProtocol.terminate();
+
+ try {
+ _messageDispatcher.terminate();
+
+ try {
+ _xConnection.close();
+ } catch (com.sun.star.io.IOException e) {
+ System.err.println(
+ getClass().getName() + ".dispose - IOException:" + e);
+ }
+
+ if (Thread.currentThread() != _messageDispatcher
+ && _messageDispatcher.isAlive())
+ {
+ _messageDispatcher.join(1000);
+ if (_messageDispatcher.isAlive()) {
+ _messageDispatcher.interrupt();
+ _messageDispatcher.join();
+ }
+ }
+
+ // interrupt all jobs queued by this bridge
+ _iThreadPool.dispose(throwable);
+
+ // release all out-mapped objects and all in-mapped proxies:
+ freeHolders();
+ // assert _java_environment instanceof java_environment;
+ ((java_environment) _java_environment).revokeAllProxies();
+
+ proxyFactory.dispose();
+
+ if (DEBUG) {
+ if (_life_count.get() != 0) {
+ System.err.println(getClass().getName()
+ + ".dispose - life count (proxies left):"
+ + _life_count);
+ }
+ _java_environment.list();
+ }
+
+ // clear members
+ _xConnection = null;
+ _java_environment = null;
+ _messageDispatcher = null;
+ } catch (InterruptedException e) {
+ System.err.println(getClass().getName()
+ + ".dispose - InterruptedException:" + e);
+ }
+ }
+
+ /**
+ *
+ * @see com.sun.star.bridge.XBridge#getInstance
+ */
+ public Object getInstance(String instanceName) {
+ Type t = new Type(XInterface.class);
+ return sendInternalRequest(
+ instanceName, t, "queryInterface", new Object[] { t });
+ }
+
+ /**
+ * Gives the name of this bridge.
+ *
+ * @return the name of this bridge.
+ * @see com.sun.star.bridge.XBridge#getName
+ */
+ public String getName() {
+ return _name;
+ }
+
+ /**
+ * Gives a description of the connection type and protocol used.
+ *
+ * @return connection type and protocol.
+ * @see com.sun.star.bridge.XBridge#getDescription
+ */
+ public String getDescription() {
+ return protocol + "," + _xConnection.getDescription();
+ }
+
+ public void sendReply(boolean exception, ThreadId threadId, Object result) {
+ if (DEBUG) {
+ System.err.println("##### " + getClass().getName() + ".sendReply: "
+ + exception + " " + result);
+ }
+
+ checkDisposed();
+
+ try {
+ _iProtocol.writeReply(exception, threadId, result);
+ } catch (IOException e) {
+ dispose(e);
+ throw (DisposedException)
+ (new DisposedException("unexpected " + e).initCause(e));
+ } catch (RuntimeException e) {
+ dispose(e);
+ throw e;
+ } catch (Error e) {
+ dispose(e);
+ throw e;
+ }
+ }
+
+ public Object sendRequest(
+ String oid, Type type, String operation, Object[] params)
+ throws Throwable
+ {
+ Object result = null;
+
+ checkDisposed();
+
+ ThreadId threadId = _iThreadPool.getThreadId();
+ Object handle = _iThreadPool.attach(threadId);
+ try {
+ boolean sync;
+ try {
+ sync = _iProtocol.writeRequest(
+ oid, TypeDescription.getTypeDescription(type), operation,
+ threadId, params);
+ } catch (IOException e) {
+ dispose(e);
+ throw (DisposedException)
+ new DisposedException(e.toString()).initCause(e);
+ }
+ if (sync && Thread.currentThread() != _messageDispatcher) {
+ result = _iThreadPool.enter(handle, threadId);
+ }
+ } finally {
+ _iThreadPool.detach(handle, threadId);
+ if(operation.equals("release"))
+ release(); // kill this bridge, if this was the last proxy
+ }
+
+ if(DEBUG) System.err.println("##### " + getClass().getName() + ".sendRequest left:" + result);
+
+ // On the wire (at least in URP), the result of queryInterface is
+ // transported as an ANY, but in Java it shall be transported as a
+ // direct reference to the UNO object (represented as a Java Object),
+ // never boxed in a com.sun.star.uno.Any:
+ if (operation.equals("queryInterface") && result instanceof Any) {
+ Any a = (Any) result;
+ if (a.getType().getTypeClass() == TypeClass.INTERFACE) {
+ result = a.getObject();
+ } else {
+ result = null; // should never happen
+ }
+ }
+
+ return result;
+ }
+
+ private Object sendInternalRequest(
+ String oid, Type type, String operation, Object[] arguments)
+ {
+ try {
+ return sendRequest(oid, type, operation, arguments);
+ } catch (Error e) {
+ throw e;
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new RuntimeException("Unexpected " + e);
+ }
+ }
+
+ /**
+ * Methods XComponent.
+ */
+ public void addEventListener(XEventListener xEventListener) {
+ _listeners.add(xEventListener);
+ }
+
+ public void removeEventListener(XEventListener xEventListener) {
+ _listeners.remove(xEventListener);
+ }
+
+ /**
+ *
+ * @see DisposeNotifier#addDisposeListener
+ */
+ public void addDisposeListener(DisposeListener listener) {
+ synchronized (this) {
+ if (!disposed) {
+ disposeListeners.add(listener);
+ return;
+ }
+ }
+ listener.notifyDispose(this);
+ }
+
+ /**
+ * This function must only be called while synchronized on this object.
+ */
+ private synchronized void checkDisposed() {
+ if (disposed) {
+ throw new DisposedException("java_remote_bridge " + this
+ + " is disposed");
+ }
+ }
+
+ private final ProxyFactory proxyFactory;
+
+ // Access to disposeListeners must be synchronized on <CODE>this</CODE>:
+ private final ArrayList<DisposeListener> disposeListeners = new ArrayList<DisposeListener>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */