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 24 package com.sun.star.lib.uno.environments.java; 25 26 import com.sun.star.uno.IEnvironment; 27 import com.sun.star.uno.Type; 28 import com.sun.star.uno.UnoRuntime; 29 import java.lang.ref.ReferenceQueue; 30 import java.lang.ref.WeakReference; 31 import java.util.HashMap; 32 import java.util.Iterator; 33 34 /** 35 * The java_environment is the environment where objects and 36 * interfaces are registered, which are mapped out of java or 37 * into java. 38 * 39 * <p>The java_environment implements the <code>IEnvironment</code> interface 40 * defined in the uno runtime.</p> 41 * 42 * @see com.sun.star.uno.UnoRuntime 43 * @see com.sun.star.uno.IEnvironment 44 * @since UDK1.0 45 */ 46 public final class java_environment implements IEnvironment { java_environment(Object context)47 public java_environment(Object context) { 48 this.context = context; 49 } 50 51 // @see com.sun.star.uno.IEnvironment#getContext getContext()52 public Object getContext() { 53 return context; 54 } 55 56 // @see com.sun.star.uno.IEnvironment#getName getName()57 public String getName() { 58 return "java"; 59 } 60 61 // @see com.sun.star.uno.IEnvironment#registerInterface registerInterface(Object object, String[] oid, Type type)62 public Object registerInterface(Object object, String[] oid, Type type) { 63 if (oid[0] == null) { 64 oid[0] = UnoRuntime.generateOid(object); 65 } 66 return (isProxy(object) ? proxies : localObjects).register( 67 object, oid[0], type); 68 } 69 70 /** 71 * You have to revoke ANY interface that has been registered via this 72 * method. 73 * 74 * @param oid object id of interface to be revoked 75 * @param type the type description of the interface 76 * @see com.sun.star.uno.IEnvironment#revokeInterface 77 */ revokeInterface(String oid, Type type)78 public void revokeInterface(String oid, Type type) { 79 if (!proxies.revoke(oid, type)) { 80 localObjects.revoke(oid, type); 81 } 82 } 83 84 /** 85 * Retrieves an interface identified by its object id and type from this 86 * environment. 87 * 88 * @param oid object id of interface to be retrieved 89 * @param type the type description of the interface to be retrieved 90 * @see com.sun.star.uno.IEnvironment#getRegisteredInterface 91 */ getRegisteredInterface(String oid, Type type)92 public Object getRegisteredInterface(String oid, Type type) { 93 Object o = proxies.get(oid, type); 94 if (o == null) { 95 o = localObjects.get(oid, type); 96 } 97 return o; 98 } 99 100 /** 101 * Retrieves the object identifier for a registered interface from this 102 * environment. 103 * 104 * @param object a registered interface 105 * @see com.sun.star.uno.IEnvironment#getRegisteredObjectIdentifier 106 */ getRegisteredObjectIdentifier(Object object)107 public String getRegisteredObjectIdentifier(Object object) { 108 return UnoRuntime.generateOid(object); 109 } 110 111 // @see com.sun.star.uno.IEnvironment#list list()112 public void list() { 113 // TODO??? 114 // synchronized (proxies) { 115 // System.err.println("##### " + getClass().getName() + ".list: " 116 // + getName() + ", " + getContext()); 117 // for (Iterator it = proxies.values().iterator(); it.hasNext();) { 118 // System.err.println("#### entry: " + it.next()); 119 // } 120 // } 121 } 122 123 /** 124 * Revokes all registered proxy interfaces. 125 * 126 * <p>This method should be part of <code>IEnvironment</code>. It is called 127 * from <code>com.sun.star.lib.uno.bridges.java_remote.<!-- 128 * -->java_remote_bridge.dispose</code>.</p> 129 */ revokeAllProxies()130 public void revokeAllProxies() { 131 proxies.clear(); 132 } 133 134 // TODO What's this??? java.lang.Object#equals requires reflexivity... 135 // 136 // Maybe this was hacked in so that different bridges use different 137 // instances of java_environment. That is desirable for the following 138 // reason: An OID is bridged in over bridge A, a proxy is created on the 139 // Java side, and recorded in the java_environment. The same OID is then 140 // bridged in over another bridge B. If there were only one 141 // java_environment shared by both bridges, the proxy from bridge A would be 142 // reused. If now bridge A is taken down programatically (e.g., because 143 // some controlling code somehow deduced that no objects are mapped over 144 // that bridge any longer), but the proxy is still used by bridge B, using 145 // the proxy would now result in errors. The explicit API to control 146 // bridges forbids to transparently share proxies between bridges, and using 147 // different java_environment instances for different bridges is the way to 148 // enforce this. equals(Object obj)149 public boolean equals(Object obj) { 150 return false; 151 } 152 153 private static final class Registry { register( Object object, String oid, Type type)154 public synchronized Object register( 155 Object object, String oid, Type type) 156 { 157 cleanUp(); 158 Level1Entry l1 = level1map.get(oid); 159 if (l1 != null) { 160 Level2Entry l2 = l1.level2map.get(type); 161 if (l2 != null) { 162 Object o = l2.get(); 163 if (o != null) { 164 l2.acquire(); 165 return o; 166 } 167 } 168 } 169 // TODO If a holder references an unreachable object, but still has 170 // a positive count, it is replaced with a new holder (referencing a 171 // reachable object, and with a count of 1). Any later calls to 172 // revoke that should decrement the count of the previous holder 173 // would now decrement the count of the new holder, removing it 174 // prematurely. This is a design flaw that will be fixed when 175 // IEnvironment.revokeInterface is changed to no longer use 176 // counting. (And this problem is harmless, as currently a holder 177 // either references a strongly held object and uses register/revoke 178 // to control it, or references a weakly held proxy and never 179 // revokes it.) 180 if (l1 == null) { 181 l1 = new Level1Entry(); 182 level1map.put(oid, l1); 183 } 184 l1.level2map.put(type, new Level2Entry(oid, type, object, queue)); 185 return object; 186 } 187 revoke(String oid, Type type)188 public synchronized boolean revoke(String oid, Type type) { 189 Level1Entry l1 = level1map.get(oid); 190 Level2Entry l2 = null; 191 if (l1 != null) { 192 l2 = l1.level2map.get(type); 193 if (l2 != null && l2.release()) { 194 removeLevel2Entry(l1, oid, type); 195 } 196 } 197 cleanUp(); 198 return l2 != null; 199 } 200 get(String oid, Type type)201 public synchronized Object get(String oid, Type type) { 202 Level1Entry l1 = level1map.get(oid); 203 return l1 == null ? null : l1.find(type); 204 } 205 clear()206 public synchronized void clear() { 207 level1map.clear(); 208 cleanUp(); 209 } 210 211 // must only be called while synchronized on this Registry: cleanUp()212 private void cleanUp() { 213 for (;;) { 214 Level2Entry l2 = (Level2Entry) queue.poll(); 215 if (l2 == null) { 216 break; 217 } 218 // It is possible that a Level2Entry e1 for the OID/type pair 219 // (o,t) becomes weakly reachable, then another Level2Entry e2 220 // is registered for the same pair (o,t) (a new Level2Entry is 221 // created since now e1.get() == null), and only then e1 is 222 // enqueued. To not erroneously remove the new e2 in that case, 223 // check whether the map still contains e1: 224 Level1Entry l1 = level1map.get(l2.oid); 225 if (l1 != null && l1.level2map.get(l2.type) == l2) { 226 removeLevel2Entry(l1, l2.oid, l2.type); 227 } 228 } 229 } 230 231 // must only be called while synchronized on this Registry: removeLevel2Entry(Level1Entry l1, String oid, Type type)232 private void removeLevel2Entry(Level1Entry l1, String oid, Type type) { 233 l1.level2map.remove(type); 234 if (l1.level2map.isEmpty()) { 235 level1map.remove(oid); 236 } 237 } 238 239 private static final class Level1Entry { 240 // must only be called while synchronized on enclosing Registry: find(Type type)241 public Object find(Type type) { 242 // First, look for an exactly matching entry; then, look for an 243 // arbitrary entry for a subtype of the request type: 244 Level2Entry l2 = level2map.get(type); 245 if (l2 != null) { 246 Object o = l2.get(); 247 if (o != null) { 248 return o; 249 } 250 } 251 for (Iterator<Level2Entry> i = level2map.values().iterator(); 252 i.hasNext();) 253 { 254 l2 = i.next(); 255 if (type.isSupertypeOf(l2.type)) { 256 Object o = l2.get(); 257 if (o != null) { 258 return o; 259 } 260 } 261 } 262 return null; 263 } 264 265 public final HashMap<Type, Level2Entry> level2map = 266 new HashMap<Type, Level2Entry>(); 267 } 268 269 private static final class Level2Entry extends WeakReference<Object> { Level2Entry( String oid, Type type, Object object, ReferenceQueue queue)270 public Level2Entry( 271 String oid, Type type, Object object, ReferenceQueue queue) 272 { 273 super(object, queue); 274 this.oid = oid; 275 this.type = type; 276 } 277 278 // must only be called while synchronized on enclosing Registry: acquire()279 public void acquire() { 280 ++count; 281 } 282 283 // must only be called while synchronized on enclosing Registry: release()284 public boolean release() { 285 return --count == 0; 286 } 287 288 public final String oid; 289 public final Type type; 290 291 private int count = 1; 292 } 293 294 private final HashMap<String, Level1Entry> level1map = 295 new HashMap<String, Level1Entry>(); 296 private final ReferenceQueue queue = new ReferenceQueue(); 297 } 298 isProxy(Object object)299 private boolean isProxy(Object object) { 300 return object instanceof com.sun.star.lib.uno.Proxy; 301 } 302 303 private static final Registry localObjects = new Registry(); 304 305 private final Object context; 306 private final Registry proxies = new Registry(); 307 } 308