blob: 7b8431e5bc546d4e730f14f030adf3a279194dfb [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
package com.sun.star.lib.uno.environments.java;
import com.sun.star.uno.IEnvironment;
import com.sun.star.uno.Type;
import com.sun.star.uno.UnoRuntime;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Iterator;
/**
* The java_environment is the environment where objects and
* interfaces are registered, which are mapped out of java or
* into java.
*
* <p>The java_environment implements the <code>IEnvironment</code> interface
* defined in the uno runtime.</p>
*
* @see com.sun.star.uno.UnoRuntime
* @see com.sun.star.uno.IEnvironment
* @since UDK1.0
*/
public final class java_environment implements IEnvironment {
public java_environment(Object context) {
this.context = context;
}
// @see com.sun.star.uno.IEnvironment#getContext
public Object getContext() {
return context;
}
// @see com.sun.star.uno.IEnvironment#getName
public String getName() {
return "java";
}
// @see com.sun.star.uno.IEnvironment#registerInterface
public Object registerInterface(Object object, String[] oid, Type type) {
if (oid[0] == null) {
oid[0] = UnoRuntime.generateOid(object);
}
return (isProxy(object) ? proxies : localObjects).register(
object, oid[0], type);
}
/**
* You have to revoke ANY interface that has been registered via this
* method.
*
* @param oid object id of interface to be revoked
* @param type the type description of the interface
* @see com.sun.star.uno.IEnvironment#revokeInterface
*/
public void revokeInterface(String oid, Type type) {
if (!proxies.revoke(oid, type)) {
localObjects.revoke(oid, type);
}
}
/**
* Retrieves an interface identified by its object id and type from this
* environment.
*
* @param oid object id of interface to be retrieved
* @param type the type description of the interface to be retrieved
* @see com.sun.star.uno.IEnvironment#getRegisteredInterface
*/
public Object getRegisteredInterface(String oid, Type type) {
Object o = proxies.get(oid, type);
if (o == null) {
o = localObjects.get(oid, type);
}
return o;
}
/**
* Retrieves the object identifier for a registered interface from this
* environment.
*
* @param object a registered interface
* @see com.sun.star.uno.IEnvironment#getRegisteredObjectIdentifier
*/
public String getRegisteredObjectIdentifier(Object object) {
return UnoRuntime.generateOid(object);
}
// @see com.sun.star.uno.IEnvironment#list
public void list() {
// TODO???
// synchronized (proxies) {
// System.err.println("##### " + getClass().getName() + ".list: "
// + getName() + ", " + getContext());
// for (Iterator it = proxies.values().iterator(); it.hasNext();) {
// System.err.println("#### entry: " + it.next());
// }
// }
}
/**
* Revokes all registered proxy interfaces.
*
* <p>This method should be part of <code>IEnvironment</code>. It is called
* from <code>com.sun.star.lib.uno.bridges.java_remote.<!--
* -->java_remote_bridge.dispose</code>.</p>
*/
public void revokeAllProxies() {
proxies.clear();
}
// TODO What's this??? java.lang.Object#equals requires reflexivity...
//
// Maybe this was hacked in so that different bridges use different
// instances of java_environment. That is desirable for the following
// reason: An OID is bridged in over bridge A, a proxy is created on the
// Java side, and recorded in the java_environment. The same OID is then
// bridged in over another bridge B. If there were only one
// java_environment shared by both bridges, the proxy from bridge A would be
// reused. If now bridge A is taken down programatically (e.g., because
// some controlling code somehow deduced that no objects are mapped over
// that bridge any longer), but the proxy is still used by bridge B, using
// the proxy would now result in errors. The explicit API to control
// bridges forbids to transparently share proxies between bridges, and using
// different java_environment instances for different bridges is the way to
// enforce this.
public boolean equals(Object obj) {
return false;
}
private static final class Registry {
public synchronized Object register(
Object object, String oid, Type type)
{
cleanUp();
Level1Entry l1 = level1map.get(oid);
if (l1 != null) {
Level2Entry l2 = l1.level2map.get(type);
if (l2 != null) {
Object o = l2.get();
if (o != null) {
l2.acquire();
return o;
}
}
}
// TODO If a holder references an unreachable object, but still has
// a positive count, it is replaced with a new holder (referencing a
// reachable object, and with a count of 1). Any later calls to
// revoke that should decrement the count of the previous holder
// would now decrement the count of the new holder, removing it
// prematurely. This is a design flaw that will be fixed when
// IEnvironment.revokeInterface is changed to no longer use
// counting. (And this problem is harmless, as currently a holder
// either references a strongly held object and uses register/revoke
// to control it, or references a weakly held proxy and never
// revokes it.)
if (l1 == null) {
l1 = new Level1Entry();
level1map.put(oid, l1);
}
l1.level2map.put(type, new Level2Entry(oid, type, object, queue));
return object;
}
public synchronized boolean revoke(String oid, Type type) {
Level1Entry l1 = level1map.get(oid);
Level2Entry l2 = null;
if (l1 != null) {
l2 = l1.level2map.get(type);
if (l2 != null && l2.release()) {
removeLevel2Entry(l1, oid, type);
}
}
cleanUp();
return l2 != null;
}
public synchronized Object get(String oid, Type type) {
Level1Entry l1 = level1map.get(oid);
return l1 == null ? null : l1.find(type);
}
public synchronized void clear() {
level1map.clear();
cleanUp();
}
// must only be called while synchronized on this Registry:
private void cleanUp() {
for (;;) {
Level2Entry l2 = (Level2Entry) queue.poll();
if (l2 == null) {
break;
}
// It is possible that a Level2Entry e1 for the OID/type pair
// (o,t) becomes weakly reachable, then another Level2Entry e2
// is registered for the same pair (o,t) (a new Level2Entry is
// created since now e1.get() == null), and only then e1 is
// enqueued. To not erroneously remove the new e2 in that case,
// check whether the map still contains e1:
Level1Entry l1 = level1map.get(l2.oid);
if (l1 != null && l1.level2map.get(l2.type) == l2) {
removeLevel2Entry(l1, l2.oid, l2.type);
}
}
}
// must only be called while synchronized on this Registry:
private void removeLevel2Entry(Level1Entry l1, String oid, Type type) {
l1.level2map.remove(type);
if (l1.level2map.isEmpty()) {
level1map.remove(oid);
}
}
private static final class Level1Entry {
// must only be called while synchronized on enclosing Registry:
public Object find(Type type) {
// First, look for an exactly matching entry; then, look for an
// arbitrary entry for a subtype of the request type:
Level2Entry l2 = level2map.get(type);
if (l2 != null) {
Object o = l2.get();
if (o != null) {
return o;
}
}
for (Iterator<Level2Entry> i = level2map.values().iterator();
i.hasNext();)
{
l2 = i.next();
if (type.isSupertypeOf(l2.type)) {
Object o = l2.get();
if (o != null) {
return o;
}
}
}
return null;
}
public final HashMap<Type, Level2Entry> level2map =
new HashMap<Type, Level2Entry>();
}
private static final class Level2Entry extends WeakReference<Object> {
public Level2Entry(
String oid, Type type, Object object, ReferenceQueue queue)
{
super(object, queue);
this.oid = oid;
this.type = type;
}
// must only be called while synchronized on enclosing Registry:
public void acquire() {
++count;
}
// must only be called while synchronized on enclosing Registry:
public boolean release() {
return --count == 0;
}
public final String oid;
public final Type type;
private int count = 1;
}
private final HashMap<String, Level1Entry> level1map =
new HashMap<String, Level1Entry>();
private final ReferenceQueue queue = new ReferenceQueue();
}
private boolean isProxy(Object object) {
return object instanceof com.sun.star.lib.uno.Proxy;
}
private static final Registry localObjects = new Registry();
private final Object context;
private final Registry proxies = new Registry();
}