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.bridges.java_remote; 25 26 import com.sun.star.bridge.XBridge; 27 import com.sun.star.lib.util.AsynchronousFinalizer; 28 import com.sun.star.uno.IQueryInterface; 29 import com.sun.star.uno.Type; 30 import com.sun.star.uno.UnoRuntime; 31 import java.lang.reflect.InvocationHandler; 32 import java.lang.reflect.Method; 33 import java.lang.reflect.Proxy; 34 35 /** 36 * A factory for proxies specific to the <code>java_remote_bridge</code>. 37 * 38 * <p>Eventually, this class should be united with all other proxy classes 39 * specific to certain bridges (for example, the JNI bridge), resulting in a 40 * generic proxy class.</p> 41 */ 42 final class ProxyFactory { ProxyFactory(RequestHandler requestHandler, XBridge bridge)43 public ProxyFactory(RequestHandler requestHandler, XBridge bridge) { 44 this.requestHandler = requestHandler; 45 this.bridge = bridge; 46 } 47 create(String oid, Type type)48 public Object create(String oid, Type type) { 49 return Proxy.newProxyInstance( 50 getClass().getClassLoader(), 51 new Class[] { com.sun.star.lib.uno.Proxy.class, 52 IQueryInterface.class, type.getZClass() }, 53 new Handler(oid, type)); 54 } 55 isProxy(Object obj)56 public boolean isProxy(Object obj) { 57 if (Proxy.isProxyClass(obj.getClass())) { 58 InvocationHandler h = Proxy.getInvocationHandler(obj); 59 return h instanceof Handler && ((Handler) h).matches(this); 60 } else { 61 return false; 62 } 63 } 64 getBridge(Object obj)65 public static XBridge getBridge(Object obj) { 66 if (Proxy.isProxyClass(obj.getClass())) { 67 InvocationHandler h = Proxy.getInvocationHandler(obj); 68 if (h instanceof Handler) { 69 return ((Handler) h).getBridge(); 70 } 71 } 72 return null; 73 } 74 getDebugCount()75 static int getDebugCount() { 76 synchronized (debugCountLock) { 77 return debugCount; 78 } 79 } 80 incrementDebugCount()81 private static void incrementDebugCount() { 82 synchronized (debugCountLock) { 83 ++debugCount; 84 } 85 } 86 decrementDebugCount()87 private static void decrementDebugCount() { 88 synchronized (debugCountLock) { 89 --debugCount; 90 } 91 } 92 93 private final class Handler implements InvocationHandler { Handler(String oid, Type type)94 public Handler(String oid, Type type) { 95 this.oid = oid; 96 this.type = type; 97 incrementDebugCount(); 98 } 99 matches(ProxyFactory factory)100 public boolean matches(ProxyFactory factory) { 101 return ProxyFactory.this == factory; 102 } 103 getBridge()104 public XBridge getBridge() { 105 return bridge; 106 } 107 invoke(Object proxy, Method method, Object[] args)108 public Object invoke(Object proxy, Method method, Object[] args) 109 throws Throwable 110 { 111 if (method.equals(METHOD_EQUALS) || method.equals(METHOD_IS_SAME)) { 112 return Boolean.valueOf(args[0] != null 113 && oid.equals(UnoRuntime.generateOid(args[0]))); 114 } else if (method.equals(METHOD_HASH_CODE)) { 115 return new Integer(oid.hashCode()); 116 } else if (method.equals(METHOD_TO_STRING)) { 117 return "[Proxy:" + System.identityHashCode(proxy) + "," + oid 118 + "," + type + "]"; 119 } else if (method.equals(METHOD_QUERY_INTERFACE)) { 120 // See the comment in java_remote_bridge.mapInterfaceTo for one 121 // reason why this implementation must not satisfy a request for 122 // a super-interface with a proxy itself: 123 return args[0].equals(type) ? proxy 124 : request("queryInterface", args); 125 } else if (method.equals(METHOD_GET_OID)) { 126 return oid; 127 } else { 128 return request(method.getName(), args); 129 } 130 } 131 finalize()132 protected void finalize() { 133 AsynchronousFinalizer.add(new AsynchronousFinalizer.Job() { 134 public void run() throws Throwable { 135 try { 136 request("release", null); 137 } finally { 138 decrementDebugCount(); 139 } 140 } 141 }); 142 } 143 request(String operation, Object[] args)144 private Object request(String operation, Object[] args) throws Throwable 145 { 146 Object res = requestHandler.sendRequest(oid, type, operation, args); 147 // Avoid early finalization of this object, while an invoke -> 148 // request call is still ongoing; as finalize also calls request, 149 // this should fulfil the condition from The Java Language 150 // Specification, 3rd ed., that "if an object's finalizer can result 151 // in synchronization on that object, then that object must be alive 152 // and considered reachable whenever a lock is held on it:" 153 synchronized (this) { 154 ++dummy; 155 } 156 return res; 157 } 158 159 private final String oid; 160 private final Type type; 161 private int dummy = 0; 162 } 163 164 private static final Method METHOD_EQUALS; 165 private static final Method METHOD_HASH_CODE; 166 private static final Method METHOD_TO_STRING; 167 private static final Method METHOD_QUERY_INTERFACE; 168 private static final Method METHOD_IS_SAME; 169 private static final Method METHOD_GET_OID; 170 static { 171 try { 172 METHOD_EQUALS = Object.class.getMethod( 173 "equals", new Class[] { Object.class }); 174 METHOD_HASH_CODE = Object.class.getMethod("hashCode", null); 175 METHOD_TO_STRING = Object.class.getMethod("toString", null); 176 METHOD_QUERY_INTERFACE = IQueryInterface.class.getMethod( 177 "queryInterface", new Class[] { Type.class }); 178 METHOD_IS_SAME = IQueryInterface.class.getMethod( 179 "isSame", new Class[] { Object.class }); 180 METHOD_GET_OID = IQueryInterface.class.getMethod("getOid", null); 181 } catch (NoSuchMethodException e) { 182 throw new ExceptionInInitializerError(e); 183 } 184 } 185 186 private static final Object debugCountLock = new Object(); 187 private static int debugCount = 0; 188 189 private final RequestHandler requestHandler; 190 private final XBridge bridge; 191 } 192