@ManagedOperation
    public V get(K key) {
        if(l1_cache != null) {
            V val=l1_cache.get(key);
            if(val != null) {
                if(log.isTraceEnabled())
                    log.trace("returned value " + val + " for " + key + " from L1 cache");
                return val;
            }
        }

        Cache.Value<V> val;
        try {
            Address dest_node=getNode(key);
            // if we are the destination, don't invoke an RPC but return the item from our L2 cache directly !
            if(dest_node.equals(local_addr)) {
                val=l2_cache.getEntry(key);
            }
            else {
                val=disp.callRemoteMethod(dest_node, new MethodCall(GET, key),
                                          new RequestOptions(ResponseMode.GET_FIRST, call_timeout));
            }
            if(val != null) {
                V retval=val.getValue();
                if(l1_cache != null && val.getTimeout() >= 0)
                    l1_cache.put(key, retval, val.getTimeout());
                return retval;
            }
            return null;
        }
        catch(Throwable t) {
            if(log.isWarnEnabled())
                log.warn("_get() failed", t);
            return null;
        }
    }