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