All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.coherence.client.RemoteCollection Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2020 Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */

package com.oracle.coherence.client;

import com.google.protobuf.ByteString;

import com.oracle.coherence.grpc.EntryResult;

import com.tangosol.net.NamedCache;
import com.tangosol.net.RequestIncompleteException;

import com.tangosol.util.ConverterCollections;
import com.tangosol.util.PagedIterator;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import java.util.concurrent.ExecutionException;

import java.util.stream.Collectors;

/**
 * A base class for implementations of collections related to a remote {@link AsyncNamedCacheClient},
 * for example key set, entry set and values.
 * 

* This {@link Collection} implementation allows removal but does not allow additions to the * collection. Methods {@link #add(Object)} {@link #addAll(Collection)} will throw an * {@link UnsupportedOperationException}. *

* Some methods in this class are intentionally inefficient partly due to their being a more efficient * means to perform the same task using the underlying {@link AsyncNamedCacheClient} and partly to ensure * that using this class on a client will not cause all of the data from the underlying * {@link AsyncNamedCacheClient} to be pulled back to the caller in one result. * * @author Jonathan Knight 2019.11.12 * @since 20.06 */ public abstract class RemoteCollection implements Collection { // ----- constructors --------------------------------------------------- protected RemoteCollection(AsyncNamedCacheClient client) { this.f_client = client; } // ----- Collection methods --------------------------------------------- @Override public int size() { try { return f_client.size().get(); } catch (InterruptedException | ExecutionException e) { throw new RequestIncompleteException(e); } } @Override public boolean isEmpty() { try { return f_client.isEmpty().get(); } catch (InterruptedException | ExecutionException e) { throw new RequestIncompleteException(e); } } @Override public boolean add(T t) { throw new UnsupportedOperationException("add operations are not supported"); } @Override public boolean addAll(Collection c) { throw new UnsupportedOperationException("add operations are not supported"); } @Override public void clear() { try { f_client.clear().get(); } catch (InterruptedException | ExecutionException e) { throw new RequestIncompleteException(e); } } @Override public boolean containsAll(Collection colKeys) { if (colKeys == null) { throw new NullPointerException("collection parameter cannot be null"); } if (colKeys.isEmpty()) { return true; } for (Object o : colKeys) { if (!contains(o)) { return false; } } return true; } @Override public boolean removeAll(Collection colKeys) { boolean fModified = false; if (size() > colKeys.size()) { for (Object colKey : colKeys) { fModified |= remove(colKey); } } else { for (Iterator iter = iterator(); iter.hasNext(); ) { if (colKeys.contains(iter.next())) { iter.remove(); fModified = true; } } } return fModified; } @Override public boolean retainAll(Collection colKeys) { boolean fModified = false; for (Iterator iter = iterator(); iter.hasNext(); ) { Object o = iter.next(); if (!colKeys.contains(o)) { iter.remove(); fModified = true; } } return fModified; } @Override public boolean equals(Object other) { if (other == this) { return true; } if (other instanceof Collection) { Collection colOther = (Collection) other; if (colOther.size() != size()) { return false; } try { return containsAll(colOther); } catch (ClassCastException | NullPointerException unused) { return false; } } return false; } @Override public int hashCode() { int h = 0; for (T obj : this) { if (obj != null) { h += obj.hashCode(); } } return h; } // ----- Object methods ------------------------------------------------- @Override public String toString() { return getClass().getSimpleName() + "(name='" + f_client.getCacheName() + "')"; } // ----- helper methods ------------------------------------------------- /** * Obtain the underlying {@link NamedCache}. * * @return the underlying {@link NamedCache} */ protected AsyncNamedCacheClient getCache() { return f_client; } /** * Create an {@link EntryAdvancer} to use in a {@link PagedIterator}. * * @return an {@link EntryAdvancer} to use in a {@link PagedIterator} */ protected EntryAdvancer createEntryAdvancer() { return new EntryAdvancer<>(getCache()); } // ----- inner class: EntryAdvancer ------------------------------------- /** * A {@link PagedIterator.Advancer} to support a * {@link PagedIterator} over an entry set. */ protected static class EntryAdvancer implements PagedIterator.Advancer { // ----- constructors ----------------------------------------------- /** * Constructs a new {@code EntryAdvancer} using the provided {@link AsyncNamedCacheClient}. * * @param client the async client */ protected EntryAdvancer(AsyncNamedCacheClient client) { this.m_client = client; } // ----- Advancer interface ----------------------------------------- @Override public void remove(Object oCurr) { Map.Entry entry = (Map.Entry) oCurr; try { m_client.removeInternal(entry.getKey()).get(); } catch (InterruptedException | ExecutionException e) { throw new RequestIncompleteException(e); } } @Override public Collection nextPage() { if (m_exhausted) { return null; } LinkedList list = m_client.getEntriesPage(m_cookie).collect(Collectors.toCollection(LinkedList::new)); if (list.size() > 0) { m_cookie = list.poll().getCookie(); } else { m_cookie = null; } m_exhausted = m_cookie == null || m_cookie.isEmpty(); // Use ConverterCollections so that deserialization is only done if for entries as they // are iterated over. This is more efficient that deserializing all of the entries // now as there is no guarantee that the caller will actually iterate over the whole // entry set or that they will call getKey() or getValue() on all entries. Collection> entries = ConverterCollections .getCollection(list, this::fromEntryResult, this::toEntryResult); return ConverterCollections.getEntrySet(entries, m_client::fromByteString, m_client::toByteString, m_client::fromByteString, m_client::toByteString); } // ----- helper methods --------------------------------------------- /** * Convert a {@link EntryResult} to a {@link Map.Entry} where the * key and value are {@link ByteString} instances. * * @param result the {@link EntryResult} to wrap in a {@link Map.Entry} * * @return a {@link Map.Entry} that wraps an {@link EntryResult} */ protected Map.Entry fromEntryResult(EntryResult result) { return new EntryResultMapEntry(result); } /** * Convert a {@link Map.Entry} to an {@link EntryResult}. * * @param entry the {@link Map.Entry} to convert * * @return an {@link EntryResult} that takes its key and value from a {@link Map.Entry} */ protected EntryResult toEntryResult(Map.Entry entry) { return EntryResult.newBuilder() .setKey(m_client.toByteString(entry.getKey())) .setValue(m_client.toByteString(entry.getValue())) .build(); } // ----- data members ----------------------------------------------- /** * A flag indicating whether this advancer has exhausted all of the pages. */ protected boolean m_exhausted; /** * The opaque cookie used by the server to maintain the page location. */ protected ByteString m_cookie; /** * The {@link AsyncNamedCacheClient} used to send gRPC requests. */ protected final AsyncNamedCacheClient m_client; } // ----- inner class: EntryResultMapEntry ------------------------------- /** * A {@link Map.Entry} implementation that wraps a {@link EntryResult}. */ protected static class EntryResultMapEntry implements Map.Entry { // ----- constructors ----------------------------------------------- /** * Create a {@link EntryResultMapEntry} wrapping a {@link EntryResult}. * * @param entryResult the {@link EntryResult} to wrap */ protected EntryResultMapEntry(EntryResult entryResult) { this.entryResult = entryResult; } // ----- Entry interface -------------------------------------------- @Override public ByteString getKey() { return entryResult.getKey(); } @Override public ByteString getValue() { return entryResult.getValue(); } @Override public ByteString setValue(ByteString value) { ByteString old = entryResult.getValue(); entryResult = entryResult.toBuilder().setValue(value).build(); return old; } // ----- data members ----------------------------------------------- /** * The {@link EntryResult}. */ private EntryResult entryResult; } // ----- data members --------------------------------------------------- /** * The wrapped {@link AsyncNamedCacheClient}. */ protected final AsyncNamedCacheClient f_client; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy