diff options
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.java | 700 |
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: */ |