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