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