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