1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 package com.sun.star.lib.uno.bridges.java_remote;
24 
25 
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.OutputStream;
29 
30 
31 import java.util.ArrayList;
32 import java.util.Enumeration;
33 import java.util.HashMap;
34 import java.util.Iterator;
35 import java.util.LinkedList;
36 import java.util.Map;
37 import java.util.Vector;
38 
39 import com.sun.star.lib.util.DisposeListener;
40 import com.sun.star.lib.util.DisposeNotifier;
41 
42 import com.sun.star.bridge.XBridge;
43 import com.sun.star.bridge.XInstanceProvider;
44 
45 import com.sun.star.connection.XConnection;
46 import com.sun.star.lang.EventObject;
47 import com.sun.star.lang.XComponent;
48 import com.sun.star.lang.XEventListener;
49 import com.sun.star.lang.DisposedException;
50 
51 import com.sun.star.lib.uno.environments.java.java_environment;
52 import com.sun.star.lib.uno.environments.remote.IProtocol;
53 import com.sun.star.lib.uno.environments.remote.IReceiver;
54 import com.sun.star.lib.uno.environments.remote.Job;
55 import com.sun.star.lib.uno.environments.remote.Message;
56 import com.sun.star.lib.uno.environments.remote.ThreadId;
57 import com.sun.star.lib.uno.environments.remote.ThreadPoolManager;
58 import com.sun.star.lib.uno.environments.remote.IThreadPool;
59 
60 import com.sun.star.lib.uno.typedesc.MethodDescription;
61 import com.sun.star.lib.uno.typedesc.TypeDescription;
62 
63 
64 import com.sun.star.uno.IBridge;
65 import com.sun.star.uno.IEnvironment;
66 import com.sun.star.uno.UnoRuntime;
67 import com.sun.star.uno.XInterface;
68 import com.sun.star.uno.Type;
69 import com.sun.star.uno.TypeClass;
70 import com.sun.star.uno.Any;
71 
72 /**
73  * This class implements a remote bridge. Therefor
74  * various interfaces are implemented.
75  * <p>
76  * The protocol to used is passed by name, the bridge
77  * then looks for it under <code>com.sun.star.lib.uno.protocols</code>.
78  * <p>
79  * @version 	$Revision: 1.45 $ $ $Date: 2008-04-11 11:18:08 $
80  * @author 	    Kay Ramme
81  * @since       UDK1.0
82  */
83 public class java_remote_bridge
84     implements IBridge, IReceiver, RequestHandler, XBridge, XComponent,
85         DisposeNotifier
86 {
87 	/**
88 	 * When set to true, enables various debugging output.
89 	 */
90 	static private final boolean DEBUG = false;
91 
92     private final class MessageDispatcher extends Thread {
MessageDispatcher()93         public MessageDispatcher() {
94             super("MessageDispatcher");
95         }
96 
run()97         public void run() {
98             try {
99                 for (;;) {
100                     synchronized (this) {
101                         if (terminate) {
102                             break;
103                         }
104                     }
105                     Message msg = _iProtocol.readMessage();
106                     Object obj = null;
107                     if (msg.isRequest()) {
108                         String oid = msg.getObjectId();
109                         Type type = new Type(msg.getType());
110                         int fid = msg.getMethod().getIndex();
111                         if (fid == MethodDescription.ID_RELEASE) {
112                             _java_environment.revokeInterface(oid, type);
113                             remRefHolder(type, oid);
114                             if (msg.isSynchronous()) {
115                                 sendReply(false, msg.getThreadId(), null);
116                             }
117                             continue;
118                         }
119                         obj = _java_environment.getRegisteredInterface(
120                             oid, type);
121                         if (obj == null
122                             && fid == MethodDescription.ID_QUERY_INTERFACE)
123                         {
124                             if (_xInstanceProvider == null) {
125                                 sendReply(
126                                     true, msg.getThreadId(),
127                                     new com.sun.star.uno.RuntimeException(
128                                         "unknown OID " + oid));
129                                 continue;
130                             } else {
131                                 UnoRuntime.setCurrentContext(
132                                     msg.getCurrentContext());
133                                 try {
134                                     obj = _xInstanceProvider.getInstance(oid);
135                                 } catch (com.sun.star.uno.RuntimeException e) {
136                                     sendReply(true, msg.getThreadId(), e);
137                                     continue;
138                                 } catch (Exception e) {
139                                     sendReply(
140                                         true, msg.getThreadId(),
141                                         new com.sun.star.uno.RuntimeException(
142                                             e.toString()));
143                                     continue;
144                                 } finally {
145                                     UnoRuntime.setCurrentContext(null);
146                                 }
147                             }
148                         }
149                     }
150                     _iThreadPool.putJob(
151                         new Job(obj, java_remote_bridge.this, msg));
152                 }
153             } catch (Throwable e) {
154                 dispose(e);
155             }
156         }
157 
terminate()158         public synchronized void terminate() {
159             terminate = true;
160         }
161 
162         private boolean terminate = false;
163     }
164 
165 	protected XConnection       _xConnection;
166 
167 	protected XInstanceProvider _xInstanceProvider;
168 
169 	protected String            _name = "remote";
170     private final String protocol;
171 	protected IProtocol         _iProtocol;
172 	protected IEnvironment      _java_environment;
173 	protected MessageDispatcher _messageDispatcher;
174 	protected int               _life_count = 0;    // determines if this bridge is alife, which is controlled by acquire and release calls
175 
176 	private final Vector _listeners = new Vector();
177 
178 	protected IThreadPool       _iThreadPool;
179 
180     // Variable disposed must only be used while synchronized on this object:
181     private boolean disposed = false;
182 
183 	/**
184 	 * This method is for testing only.
185 	 */
getLifeCount()186 	int getLifeCount() {
187 		return _life_count;
188 	}
189 
190 	/**
191 	 * This method is for testing only.
192 	 */
getProtocol()193 	IProtocol getProtocol() {
194 		return _iProtocol;
195 	}
196 
197     // The ref holder stuff strongly holds objects mapped out via this bridge
198     // (the java_environment only holds them weakly).  When this bridge is
199     // disposed, all remaining ref holder entries are released.
200 
201     private static final class RefHolder {
RefHolder(Type type, Object object)202         public RefHolder(Type type, Object object) {
203             this.type = type;
204             this.object = object;
205         }
206 
getType()207         public Type getType() {
208             return type;
209         }
210 
acquire()211         public void acquire() {
212             ++count;
213         }
214 
release()215         public boolean release() {
216             return --count == 0;
217         }
218 
219         private final Type type;
220         private final Object object;
221         private int count = 1;
222     }
223 
224 	private final HashMap refHolders = new HashMap();
225         // from OID (String) to LinkedList of RefHolder
226 
hasRefHolder(String oid, Type type)227     private boolean hasRefHolder(String oid, Type type) {
228         synchronized (refHolders) {
229             LinkedList l = (LinkedList) refHolders.get(oid);
230             if (l != null) {
231                 for (Iterator i = l.iterator(); i.hasNext();) {
232                     RefHolder rh = (RefHolder) i.next();
233                     if (type.isSupertypeOf(rh.getType())) {
234                         return true;
235                     }
236                 }
237             }
238         }
239         return false;
240     }
241 
addRefHolder(Object obj, Type type, String oid)242     final void addRefHolder(Object obj, Type type, String oid) {
243         synchronized (refHolders) {
244             LinkedList l = (LinkedList) refHolders.get(oid);
245             if (l == null) {
246                 l = new LinkedList();
247                 refHolders.put(oid, l);
248             }
249             boolean found = false;
250             for (Iterator i = l.iterator(); !found && i.hasNext();) {
251                 RefHolder rh = (RefHolder) i.next();
252                 if (rh.getType().equals(type)) {
253                     found = true;
254                     rh.acquire();
255                 }
256             }
257             if (!found) {
258                 l.add(new RefHolder(type, obj));
259             }
260         }
261         acquire();
262     }
263 
remRefHolder(Type type, String oid)264     final void remRefHolder(Type type, String oid) {
265         synchronized (refHolders) {
266             LinkedList l = (LinkedList) refHolders.get(oid);
267             if (l != null) {
268                 for (Iterator i = l.iterator(); i.hasNext();) {
269                     RefHolder rh = (RefHolder) i.next();
270                     if (rh.getType().equals(type)) {
271                         try {
272                             if (rh.release()) {
273                                 l.remove(rh);
274                                 if (l.isEmpty()) {
275                                     refHolders.remove(oid);
276                                 }
277                             }
278                         } finally {
279                             release();
280                         }
281                         break;
282                     }
283                 }
284             }
285         }
286     }
287 
freeHolders()288     final void freeHolders() {
289         synchronized (refHolders) {
290             for (Iterator i1 = refHolders.entrySet().iterator(); i1.hasNext();)
291             {
292                 Map.Entry e = (Map.Entry) i1.next();
293                 String oid = (String) e.getKey();
294                 LinkedList l = (LinkedList) e.getValue();
295                 for (Iterator i2 = l.iterator(); i2.hasNext();) {
296                     RefHolder rh = (RefHolder) i2.next();
297                     for (boolean done = false; !done;) {
298                         done = rh.release();
299                         _java_environment.revokeInterface(oid, rh.getType());
300                         release();
301                     }
302                 }
303             }
304             refHolders.clear();
305         }
306     }
307 
java_remote_bridge( IEnvironment java_environment, IEnvironment remote_environment, Object[] args)308     public java_remote_bridge(
309         IEnvironment java_environment, IEnvironment remote_environment,
310         Object[] args)
311         throws Exception
312     {
313         _java_environment = java_environment;
314         String proto = (String) args[0];
315         _xConnection = (XConnection) args[1];
316         _xInstanceProvider = (XInstanceProvider) args[2];
317         if (args.length > 3) {
318             _name = (String) args[3];
319         }
320         String attr;
321         int i = proto.indexOf(',');
322         if (i >= 0) {
323             protocol = proto.substring(0, i);
324             attr = proto.substring(i + 1);
325         } else {
326             protocol = proto;
327             attr = null;
328         }
329         _iProtocol = (IProtocol) Class.forName(
330             "com.sun.star.lib.uno.protocols." + protocol + "." + protocol).
331             getConstructor(
332                 new Class[] {
333                     IBridge.class, String.class, InputStream.class,
334                     OutputStream.class }).
335             newInstance(
336                 new Object[] {
337                     this, attr,
338                     new XConnectionInputStream_Adapter(_xConnection),
339                     new XConnectionOutputStream_Adapter(_xConnection) });
340         proxyFactory = new ProxyFactory(this, this);
341         _iThreadPool = ThreadPoolManager.create();
342         _messageDispatcher = new MessageDispatcher();
343         _messageDispatcher.start();
344         _iProtocol.init();
345     }
346 
notifyListeners()347 	private void notifyListeners() {
348 		EventObject eventObject = new EventObject(this);
349 
350 		Enumeration elements = _listeners.elements();
351 		while(elements.hasMoreElements()) {
352 			XEventListener xEventListener = (XEventListener)elements.nextElement();
353 
354 			try {
355 				xEventListener.disposing(eventObject);
356 			}
357 			catch(com.sun.star.uno.RuntimeException runtimeException) {
358 				// we are here not interested in any exceptions
359 			}
360 		}
361 	}
362 
363 	/**
364 	 * Constructs a new bridge.
365 	 * <p>
366 	 * This method is not part of the provided <code>api</code>
367 	 * and should only be used by the UNO runtime.
368 	 * <p>
369 	 * @deprecated as of UDK 1.0
370 	 * <p>
371 	 * @param  args               the custom parameters: arg[0] == protocol_name, arg[1] == xConnection, arg[2] == xInstanceProvider
372 	 */
java_remote_bridge(Object args[])373 	public java_remote_bridge(Object args[]) throws Exception {
374 		this(UnoRuntime.getEnvironment("java", null), UnoRuntime.getEnvironment("remote", null), args);
375 	}
376 
377     // @see com.sun.star.uno.IBridge#mapInterfaceTo
mapInterfaceTo(Object object, Type type)378     public Object mapInterfaceTo(Object object, Type type) {
379         checkDisposed();
380         if (object == null) {
381             return null;
382         } else {
383             String[] oid = new String[1];
384             object = _java_environment.registerInterface(object, oid, type);
385             if (!proxyFactory.isProxy(object)) {
386                 // This branch must be taken iff object either is no proxy at
387                 // all or a proxy from some other bridge.  There are objects
388                 // that behave like objects for this bridge but that are not
389                 // detected as such by proxyFactory.isProxy.  The only known
390                 // case of such objects is com.sun.star.comp.beans.Wrapper,
391                 // which implements com.sun.star.lib.uno.Proxy and effectively
392                 // is a second proxy around a proxy that can be from this
393                 // bridge.  For that case, there is no problem, however:  Since
394                 // the proxies generated by ProxyFactory send each
395                 // queryInterface to the original object (i.e., they do not
396                 // short-circuit requests for a super-interface to themselves),
397                 // there will always be an appropriate ProxyFactory-proxy
398                 // registered at the _java_environment, so that the object
399                 // returned by _java_environment.registerInterface will never be
400                 // a com.sun.star.comp.beans.Wrapper.
401                 addRefHolder(object, type, oid[0]);
402             }
403             return oid[0];
404         }
405 	}
406 
407 	/**
408 	 * Maps an object from destination environment to the source environment.
409 	 * <p>
410 	 * @return     the object in the source environment
411 	 * @param      object     the object to map
412 	 * @param      type       the interface under which is to be mapped
413 	 * @see                   com.sun.star.uno.IBridge#mapInterfaceFrom
414 	 */
mapInterfaceFrom(Object oId, Type type)415 	public Object mapInterfaceFrom(Object oId, Type type) {
416         checkDisposed();
417         // TODO  What happens if an exception is thrown after the call to
418         // acquire, but before it is guaranteed that a pairing release will be
419         // called eventually?
420 		acquire();
421         String oid = (String) oId;
422 		Object object = _java_environment.getRegisteredInterface(oid, type);
423         if (object == null) {
424 			object = _java_environment.registerInterface(
425                 proxyFactory.create(oid, type), new String[] { oid }, type);
426                 // the proxy sends a release when finalized
427         } else if (!hasRefHolder(oid, type)) {
428             sendInternalRequest(oid, type, "release", null);
429         }
430 		return object;
431 	}
432 
433 	/**
434 	 * Gives the source environment.
435 	 * <p>
436 	 * @return   the source environment of this bridge
437 	 * @see      com.sun.star.uno.IBridge#getSourceEnvironment
438 	 */
getSourceEnvironment()439 	public IEnvironment getSourceEnvironment() {
440 		return _java_environment;
441 	}
442 
443 	/**
444 	 * Gives the destination environment.
445 	 * <p>
446 	 * @return   the destination environment of this bridge
447 	 * @see      com.sun.star.uno.IBridge#getTargetEnvironment
448 	 */
getTargetEnvironment()449 	public IEnvironment getTargetEnvironment() {
450 		return null;
451 	}
452 
453 	/**
454 	 * Increases the life count.
455 	 * <p>
456 	 * @see com.sun.star.uno.IBridge#acquire
457 	 */
acquire()458 	public synchronized void acquire() {
459 		++ _life_count;
460 
461 		if(DEBUG) System.err.println("##### " + getClass().getName() + ".acquire:" + _life_count);
462 	}
463 
464 	/**
465 	 * Decreases the life count.
466 	 * If the life count drops to zero, the bridge disposes itself.
467 	 * <p>
468 	 * @see com.sun.star.uno.IBridge#release
469 	 */
release()470     public void release() {
471         boolean dispose;
472         synchronized (this) {
473             --_life_count;
474             dispose = _life_count <= 0;
475         }
476         if (dispose) {
477             dispose(new Throwable("end of life"));
478         }
479     }
480 
dispose()481 	public void dispose() {
482 		dispose(new Throwable("user dispose"));
483 	}
484 
dispose(Throwable throwable)485     private void dispose(Throwable throwable) {
486         synchronized (this) {
487             if (disposed) {
488                 return;
489             }
490             disposed = true;
491         }
492 
493         notifyListeners();
494         for (Iterator i = disposeListeners.iterator(); i.hasNext();) {
495             ((DisposeListener) i.next()).notifyDispose(this);
496         }
497 
498         _iProtocol.terminate();
499 
500         try {
501             _messageDispatcher.terminate();
502 
503             _xConnection.close();
504 
505             if (Thread.currentThread() != _messageDispatcher
506                 && _messageDispatcher.isAlive())
507             {
508                 // This is a workaround for a Linux Sun JDK1.3 problem:  The
509                 // message dispatcher stays in the socket read method, even if
510                 // the socket has been closed.  Suspending and resuming the
511                 // message dispatcher lets it notice the closed socket.  Only
512                 // use this workaround for Linux JRE 1.3.0 and 1.3.1 from Sun
513                 // and Blackdown.  This workaround is dangerouse and may
514                 // hardlock the VM.
515                 if (System.getProperty("os.name", "").toLowerCase().equals(
516                         "linux")
517 					&& System.getProperty("java.version", "").startsWith("1.3.")
518 					&& (System.getProperty("java.vendor", "").toLowerCase().
519                             indexOf("sun") != -1
520                         || System.getProperty("java.vendor", "").toLowerCase().
521                             indexOf("blackdown") != -1))
522                 {
523                     _messageDispatcher.suspend();
524                     _messageDispatcher.resume();
525                 }
526 
527                 _messageDispatcher.join(1000);
528                 if (_messageDispatcher.isAlive()) {
529                     _messageDispatcher.interrupt();
530                     _messageDispatcher.join();
531                 }
532             }
533 
534             // interrupt all jobs queued by this bridge
535             _iThreadPool.dispose(throwable);
536 
537             // release all out-mapped objects and all in-mapped proxies:
538             freeHolders();
539             // assert _java_environment instanceof java_environment;
540             ((java_environment) _java_environment).revokeAllProxies();
541 
542             if (DEBUG) {
543 				if (_life_count != 0) {
544 					System.err.println(getClass().getName()
545                                        + ".dispose - life count (proxies left):"
546                                        + _life_count);
547                 }
548                 _java_environment.list();
549             }
550 
551             // clear members
552             _xConnection        = null;
553             _java_environment   = null;
554             _messageDispatcher  = null;
555         } catch (InterruptedException e) {
556             System.err.println(getClass().getName()
557                                + ".dispose - InterruptedException:" + e);
558         } catch (com.sun.star.io.IOException e) {
559             System.err.println(getClass().getName() + ".dispose - IOException:"
560                                + e);
561         }
562 	}
563 
564     // @see com.sun.star.bridge.XBridge#getInstance
getInstance(String instanceName)565     public Object getInstance(String instanceName) {
566         Type t = new Type(XInterface.class);
567         return sendInternalRequest(
568             instanceName, t, "queryInterface", new Object[] { t });
569     }
570 
571 	/**
572 	 * Gives the name of this bridge
573 	 * <p>
574 	 * @return  the name of this bridge
575 	 * @see     com.sun.star.bridge.XBridge#getName
576 	 */
getName()577     public String getName() {
578 		return _name;
579 	}
580 
581 	/**
582 	 * Gives a description of the connection type and protocol used
583 	 * <p>
584 	 * @return  connection type and protocol
585 	 * @see     com.sun.star.bridge.XBridge#getDescription
586 	 */
getDescription()587     public String getDescription() {
588         return protocol + "," + _xConnection.getDescription();
589 	}
590 
sendReply(boolean exception, ThreadId threadId, Object result)591     public void sendReply(boolean exception, ThreadId threadId, Object result) {
592         if (DEBUG) {
593             System.err.println("##### " + getClass().getName() + ".sendReply: "
594                                + exception + " " + result);
595         }
596 
597         checkDisposed();
598 
599         try {
600             _iProtocol.writeReply(exception, threadId, result);
601         } catch (IOException e) {
602             dispose(e);
603             throw (DisposedException)
604                 (new DisposedException("unexpected " + e).initCause(e));
605         } catch (RuntimeException e) {
606             dispose(e);
607             throw e;
608         } catch (Error e) {
609             dispose(e);
610             throw e;
611         }
612     }
613 
sendRequest( String oid, Type type, String operation, Object[] params)614     public Object sendRequest(
615         String oid, Type type, String operation, Object[] params)
616         throws Throwable
617     {
618 		Object result = null;
619 
620         checkDisposed();
621 
622         ThreadId threadId = _iThreadPool.getThreadId();
623         Object handle = _iThreadPool.attach(threadId);
624         try {
625             boolean sync;
626             try {
627                 sync = _iProtocol.writeRequest(
628                     oid, TypeDescription.getTypeDescription(type), operation,
629                     threadId, params);
630             } catch (IOException e) {
631                 dispose(e);
632                 throw (DisposedException)
633                     new DisposedException(e.toString()).initCause(e);
634             }
635             if (sync && Thread.currentThread() != _messageDispatcher) {
636                 result = _iThreadPool.enter(handle, threadId);
637             }
638         } finally {
639             _iThreadPool.detach(handle, threadId);
640 			if(operation.equals("release"))
641 				release(); // kill this bridge, if this was the last proxy
642 		}
643 
644 		if(DEBUG) System.err.println("##### " + getClass().getName() + ".sendRequest left:" + result);
645 
646         // On the wire (at least in URP), the result of queryInterface is
647         // transported as an ANY, but in Java it shall be transported as a
648         // direct reference to the UNO object (represented as a Java Object),
649         // never boxed in a com.sun.star.uno.Any:
650         if (operation.equals("queryInterface") && result instanceof Any) {
651             Any a = (Any) result;
652             if (a.getType().getTypeClass() == TypeClass.INTERFACE) {
653                 result = a.getObject();
654             } else {
655                 result = null; // should never happen
656             }
657         }
658 
659 		return result;
660 	}
661 
sendInternalRequest( String oid, Type type, String operation, Object[] arguments)662     private Object sendInternalRequest(
663         String oid, Type type, String operation, Object[] arguments)
664     {
665         try {
666             return sendRequest(oid, type, operation, arguments);
667         } catch (Error e) {
668             throw e;
669         } catch (RuntimeException e) {
670             throw e;
671         } catch (Throwable e) {
672             throw new RuntimeException("Unexpected " + e);
673         }
674     }
675 
676     // Methods XComponent
addEventListener(XEventListener xEventListener)677     public void addEventListener(XEventListener xEventListener) {
678 		_listeners.addElement(xEventListener);
679 	}
680 
removeEventListener(XEventListener xEventListener)681     public void removeEventListener(XEventListener xEventListener) {
682 		_listeners.removeElement(xEventListener);
683 	}
684 
685     // @see NotifyDispose.addDisposeListener
addDisposeListener(DisposeListener listener)686     public void addDisposeListener(DisposeListener listener) {
687         synchronized (this) {
688             if (!disposed) {
689                 disposeListeners.add(listener);
690                 return;
691             }
692         }
693         listener.notifyDispose(this);
694     }
695 
696     // This function must only be called while synchronized on this object:
checkDisposed()697     private synchronized void checkDisposed() {
698         if (disposed) {
699             throw new DisposedException("java_remote_bridge " + this
700                                         + " is disposed");
701         }
702     }
703 
704     private final ProxyFactory proxyFactory;
705 
706     // Access to disposeListeners must be synchronized on <CODE>this</CODE>:
707     private final ArrayList disposeListeners = new ArrayList();
708 }
709