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 import com.sun.star.bridge.XBridge; 26 import com.sun.star.bridge.XInstanceProvider; 27 import com.sun.star.comp.connections.PipedConnection; 28 import com.sun.star.connection.XConnection; 29 import com.sun.star.container.NoSuchElementException; 30 import com.sun.star.lib.uno.environments.java.java_environment; 31 import com.sun.star.lib.uno.typeinfo.MethodTypeInfo; 32 import com.sun.star.lib.uno.typeinfo.TypeInfo; 33 import com.sun.star.uno.IQueryInterface; 34 import com.sun.star.uno.Type; 35 import com.sun.star.uno.UnoRuntime; 36 import com.sun.star.uno.XInterface; 37 import complexlib.ComplexTestCase; 38 import util.WaitUnreachable; 39 40 public final class java_remote_bridge_Test extends ComplexTestCase { getTestObjectName()41 public String getTestObjectName() { 42 return getClass().getName(); 43 } 44 getTestMethodNames()45 public String[] getTestMethodNames() { 46 return new String[] { "test" }; 47 } 48 test()49 public void test() throws Exception { 50 String protocol = "urp"; 51 52 XConnection connectionA = new PipedConnection(new Object[0]); 53 XConnection connectionB = new PipedConnection( 54 new Object[] { connectionA }); 55 java_remote_bridge bridgeA = new java_remote_bridge( 56 new java_environment(null), null, 57 new Object[] { protocol, connectionA, new TestInstanceProvider() }); 58 java_remote_bridge bridgeB = new java_remote_bridge( 59 new java_environment(null), null, 60 new Object[] { protocol, connectionB, null }); 61 62 testGetInstance(bridgeA, bridgeB); 63 testLifeCycle(bridgeA, bridgeB); 64 } 65 testGetInstance(XBridge bridgeA, XBridge bridgeB)66 private void testGetInstance(XBridge bridgeA, XBridge bridgeB) { 67 assure("return null", 68 bridgeB.getInstance(TestInstanceProvider.NAME_NULL) == null); 69 70 try { 71 bridgeB.getInstance(TestInstanceProvider.NAME_RUNTIME_EXCEPTION); 72 failed("throw RuntimeException"); 73 } catch (com.sun.star.uno.RuntimeException e) { 74 assure("throw RuntimeException", 75 e.getMessage().indexOf( 76 TestInstanceProvider.NAME_RUNTIME_EXCEPTION) != -1); 77 } 78 79 try { 80 bridgeB.getInstance( 81 TestInstanceProvider.NAME_NO_SUCH_ELEMENT_EXCEPTION); 82 failed("throw NoSuchElementException"); 83 } catch (com.sun.star.uno.RuntimeException e) { 84 assure("throw NoSuchElementException", 85 e.getMessage().indexOf( 86 TestInstanceProvider.NAME_NO_SUCH_ELEMENT_EXCEPTION) 87 != -1); 88 } 89 90 try { 91 bridgeA.getInstance(TestInstanceProvider.NAME_ANYTHING); 92 failed("no instance provider"); 93 } catch (com.sun.star.uno.RuntimeException e) { 94 assure("no instance provider", 95 e.getMessage().startsWith("unknown OID ")); 96 } 97 } 98 testLifeCycle(java_remote_bridge bridgeA, java_remote_bridge bridgeB)99 private void testLifeCycle(java_remote_bridge bridgeA, 100 java_remote_bridge bridgeB) 101 throws InterruptedException 102 { 103 // Repeatedly, objects are mapped from bridgeA to bridgeB, where proxies 104 // for those objects (for the XInterface and TestInterface facets) are 105 // created. The proxies at bridgeB keep both bridges alive; after those 106 // proxies have been garbage-collected, both bridges should be disposed. 107 // It does not work to map a local object from bridgeA to bridgeB, as 108 // bridgeB would find this object as a local one, too (via the shared, 109 // static localObjects Registry in java_environment): bridgeB would not 110 // create a proxy, would rather send back a "release" to bridgeA, and 111 // both bridges would be disposed while the first object is being 112 // mapped. Therefore, a HACK is used to install TestProxy objects 113 // (which behave as if they got mapped in to bridgeA from somewhere 114 // else) at bridgeA and map those. 115 116 final int COUNT = 100; 117 XInterface[] proxyBXInterface = new XInterface[COUNT]; 118 TestInterface[] proxyBTestInterface = new TestInterface[COUNT]; 119 for (int i = 0; i < COUNT; ++i) { 120 String name = "TestOID" + i; 121 Object proxyA = new TestProxy(name); 122 bridgeA.getSourceEnvironment().registerInterface( 123 proxyA, new String[] { name }, new Type(XInterface.class)); 124 125 proxyBXInterface[i] = (XInterface) bridgeB.getInstance(name); 126 127 // map object: 128 proxyBTestInterface[i] = UnoRuntime.queryInterface( 129 TestInterface.class, proxyBXInterface[i]); 130 proxyBTestInterface[i].function(); 131 132 // remap object once: 133 TestInterface remapped = UnoRuntime.queryInterface( 134 TestInterface.class, proxyBXInterface[i]); 135 remapped.function(); 136 137 // remap object twice: 138 remapped = UnoRuntime.queryInterface( 139 TestInterface.class, proxyBXInterface[i]); 140 remapped.function(); 141 } 142 143 assure("calls of object method", TestProxy.getCount() == 3 * COUNT); 144 145 // The following checks rely on the implementation detail that mapping 146 // different facets of a UNO object (XInterface and TestInterface) leads 147 // to different proxies: 148 149 assure("bridge A life count", bridgeA.getLifeCount() == 2 * COUNT); 150 assure("bridge B life count", bridgeB.getLifeCount() == 2 * COUNT); 151 assure("proxy count", ProxyFactory.getDebugCount() == 2 * COUNT); 152 153 System.out.println("waiting for proxies to become unreachable:"); 154 for (int i = 0; i < COUNT; ++i) { 155 WaitUnreachable u1 = new WaitUnreachable(proxyBXInterface[i]); 156 WaitUnreachable u2 = new WaitUnreachable(proxyBTestInterface[i]); 157 proxyBXInterface[i] = null; 158 proxyBTestInterface[i] = null; 159 u1.waitUnreachable(); 160 u2.waitUnreachable(); 161 } 162 // For whatever strange reason, this sleep seems to be necessary to 163 // reliably ensure that even the last proxy's finalization is over 164 // before the following assure is executed: 165 Thread.sleep(1000); 166 167 assure("proxy count", ProxyFactory.getDebugCount() == 0); 168 169 System.out.println("waiting for pending messages to be done"); 170 while (bridgeA.getLifeCount() != 0 || bridgeB.getLifeCount() != 0) { 171 Thread.sleep(100); 172 } 173 174 assure("Zero bridge A life count", bridgeA.getLifeCount() == 0); 175 assure("Zero bridge B life count", bridgeB.getLifeCount() == 0); 176 assure("Zero proxy count", ProxyFactory.getDebugCount() == 0); 177 } 178 179 public interface TestInterface extends XInterface { function()180 void function(); 181 182 TypeInfo[] UNOTYPEINFO = new TypeInfo[] { 183 new MethodTypeInfo("function", 0, 0) }; 184 } 185 186 private static final class TestInstanceProvider 187 implements XInstanceProvider 188 { getInstance(String name)189 public Object getInstance(String name) throws NoSuchElementException { 190 if (name.equals(NAME_NULL)) { 191 return null; 192 } else if (name.equals(NAME_RUNTIME_EXCEPTION)) { 193 throw new com.sun.star.uno.RuntimeException( 194 getClass().getName() + ", throwing: " + name); 195 } else if (name.equals(NAME_NO_SUCH_ELEMENT_EXCEPTION)) { 196 throw new NoSuchElementException( 197 getClass().getName() + ", throwing: " + name); 198 } else { 199 throw new IllegalStateException(); 200 } 201 } 202 203 public static final String NAME_NULL = "return null"; 204 public static final String NAME_RUNTIME_EXCEPTION 205 = "throw RuntimeException"; 206 public static final String NAME_NO_SUCH_ELEMENT_EXCEPTION 207 = "throw NoSuchElementException"; 208 public static final String NAME_ANYTHING = "anything"; 209 } 210 211 private static final class TestProxy 212 implements com.sun.star.lib.uno.Proxy, IQueryInterface, XInterface, 213 TestInterface 214 { TestProxy(String oid)215 public TestProxy(String oid) { 216 this.oid = oid; 217 } 218 queryInterface(Type type)219 public Object queryInterface(Type type) { 220 // type should be either XInterface or TestInterface... 221 return this; 222 } 223 isSame(Object object)224 public boolean isSame(Object object) { 225 return object instanceof TestProxy 226 && oid.equals(((TestProxy) object).oid); 227 } 228 getOid()229 public String getOid() { 230 return oid; 231 } 232 function()233 public void function() { 234 synchronized (getClass()) { 235 ++count; 236 } 237 } 238 getCount()239 public static synchronized int getCount() { 240 return count; 241 } 242 243 private final String oid; 244 245 private static int count = 0; 246 } 247 } 248