001package co.codewizards.cloudstore.ls.core.invoke;
002
003import static java.util.Objects.*;
004
005import java.lang.ref.Reference;
006import java.lang.ref.ReferenceQueue;
007import java.lang.ref.WeakReference;
008import java.util.HashMap;
009import java.util.IdentityHashMap;
010import java.util.Map;
011
012import org.slf4j.Logger;
013import org.slf4j.LoggerFactory;
014
015public class RemoteObjectProxyManager {
016
017        private static final Logger logger = LoggerFactory.getLogger(RemoteObjectProxyManager.class);
018
019        private final Map<ObjectRef, WeakReference<RemoteObjectProxy>> objectRef2RemoteObjectProxyRef = new HashMap<>();
020        private final Map<WeakReference<RemoteObjectProxy>, ObjectRef> remoteObjectProxyRef2ObjectRef = new IdentityHashMap<>();
021        private final ReferenceQueue<RemoteObjectProxy> referenceQueue = new ReferenceQueue<RemoteObjectProxy>();
022
023        protected RemoteObjectProxyManager() {
024                if (logger.isDebugEnabled())
025                        logger.debug("[{}]<init>", getThisId());
026        }
027
028        public synchronized RemoteObjectProxy getRemoteObjectProxy(final ObjectRef objectRef) {
029                requireNonNull(objectRef, "objectRef");
030                final WeakReference<RemoteObjectProxy> remoteObjectProxyRef = objectRef2RemoteObjectProxyRef.get(objectRef);
031                final RemoteObjectProxy remoteObjectProxy = remoteObjectProxyRef == null ? null : remoteObjectProxyRef.get();
032                evictOrphanedObjectRefs();
033                return remoteObjectProxy;
034        }
035
036        public synchronized RemoteObjectProxy getRemoteObjectProxyOrCreate(final ObjectRef objectRef, final RemoteObjectProxyFactory remoteObjectProxyFactory) {
037                requireNonNull(objectRef, "objectRef");
038                requireNonNull(remoteObjectProxyFactory, "remoteObjectProxyFactory");
039
040                final WeakReference<RemoteObjectProxy> remoteObjectProxyRef = objectRef2RemoteObjectProxyRef.get(objectRef);
041                RemoteObjectProxy remoteObjectProxy = remoteObjectProxyRef == null ? null : remoteObjectProxyRef.get();
042
043                if (remoteObjectProxy == null) {
044                        if (logger.isDebugEnabled())
045                                logger.debug("[{}]getRemoteObjectProxy: Creating proxy for {}. remoteObjectProxyRef={}", getThisId(), objectRef, remoteObjectProxyRef);
046
047                        // We do not need to create the proxy outside of the synchronized block, anymore, because the proxy creation now works without
048                        // immediate inverse-invocation and thus there's no more risk of a deadlock, here. => stay inside single big synchronized-block.
049                        remoteObjectProxy = remoteObjectProxyFactory.createRemoteObjectProxy(objectRef);
050                        requireNonNull(remoteObjectProxy, "remoteObjectProxyFactory.createRemoteObjectProxy(objectRef)");
051
052                        final WeakReference<RemoteObjectProxy> reference = new WeakReference<>(remoteObjectProxy, referenceQueue);
053                        objectRef2RemoteObjectProxyRef.put(objectRef, reference);
054                        remoteObjectProxyRef2ObjectRef.put(reference, objectRef);
055                }
056                evictOrphanedObjectRefs();
057                return remoteObjectProxy;
058        }
059
060        private String getThisId() {
061                return Integer.toHexString(System.identityHashCode(this));
062        }
063
064        private synchronized void evictOrphanedObjectRefs() {
065                Reference<? extends RemoteObjectProxy> reference;
066                while (null != (reference = referenceQueue.poll())) {
067                        final ObjectRef objectRef = remoteObjectProxyRef2ObjectRef.remove(reference);
068                        objectRef2RemoteObjectProxyRef.remove(objectRef);
069                }
070        }
071}