/*
* Copyright 2012-2018 Chronicle Map Contributors
*
* Licensed 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 net.openhft.chronicle.set;
import net.openhft.chronicle.hash.ChronicleHashBuilder;
import net.openhft.chronicle.hash.ChronicleHashBuilderPrivateAPI;
import net.openhft.chronicle.hash.ChronicleHashCorruption;
import net.openhft.chronicle.hash.Data;
import net.openhft.chronicle.hash.serialization.*;
import net.openhft.chronicle.map.*;
import net.openhft.chronicle.map.replication.MapRemoteOperations;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
/**
* {@code ChronicleSetBuilder} manages the whole set of {@link ChronicleSet} configurations, could
* be used as a classic builder and/or factory.
*
*
{@code ChronicleMapBuilder} is mutable, see a note in {@link
* ChronicleHashBuilder} interface documentation.
*
* @param element type of the sets, created by this builder
* @see ChronicleSet
* @see ChronicleMapBuilder
*/
public final class ChronicleSetBuilder
implements ChronicleHashBuilder, ChronicleSetBuilder> {
private static final Logger chronicleSetLogger = LoggerFactory.getLogger(ChronicleSet.class);
private static final ChronicleHashCorruption.Listener defaultChronicleSetCorruptionListener =
corruption -> {
if (corruption.exception() != null) {
chronicleSetLogger.error(corruption.message(), corruption.exception());
} else {
chronicleSetLogger.error(corruption.message());
}
};
private ChronicleMapBuilder chronicleMapBuilder;
private ChronicleSetBuilderPrivateAPI privateAPI;
ChronicleSetBuilder(Class keyClass) {
chronicleMapBuilder = ChronicleMapBuilder.of(keyClass, DummyValue.class)
.valueReaderAndDataAccess(
DummyValueMarshaller.INSTANCE, DummyValueMarshaller.INSTANCE)
.valueSizeMarshaller(SizeMarshaller.constant(0));
//noinspection deprecation,unchecked
privateAPI = new ChronicleSetBuilderPrivateAPI<>(
(ChronicleHashBuilderPrivateAPI>)
chronicleMapBuilder.privateAPI());
}
/**
* Returns a new {@code ChronicleSetBuilder} instance which is able to {@linkplain #create()
* create} sets with the specified key class.
*
* @param keyClass class object used to infer key type and discover it's properties via
* reflection
* @param key type of the sets, created by the returned builder
* @return a new builder for the given key class
*/
public static ChronicleSetBuilder of(Class keyClass) {
return new ChronicleSetBuilder<>(keyClass);
}
@Override
public ChronicleSetBuilder clone() {
try {
@SuppressWarnings("unchecked") final ChronicleSetBuilder result = (ChronicleSetBuilder) super.clone();
result.chronicleMapBuilder = chronicleMapBuilder.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
@Override
public ChronicleSetBuilder name(String name) {
chronicleMapBuilder.name(name);
return this;
}
@Override
public ChronicleSetBuilder actualSegments(int actualSegments) {
chronicleMapBuilder.actualSegments(actualSegments);
return this;
}
@Override
public ChronicleSetBuilder minSegments(int minSegments) {
chronicleMapBuilder.minSegments(minSegments);
return this;
}
@Override
public ChronicleSetBuilder entriesPerSegment(long entriesPerSegment) {
chronicleMapBuilder.entriesPerSegment(entriesPerSegment);
return this;
}
@Override
public ChronicleSetBuilder actualChunksPerSegmentTier(long actualChunksPerSegmentTier) {
chronicleMapBuilder.actualChunksPerSegmentTier(actualChunksPerSegmentTier);
return this;
}
/**
* {@inheritDoc}
*
*
Example: if keys in your set(s) are English words in {@link String} form, average English
* word length is 5.1, configure average key size of 6:
{@code
* ChronicleSet uniqueWords = ChronicleSetBuilder.of(String.class)
* .entries(50000)
* .averageKeySize(6)
* .create();}
*
*
(Note that 6 is chosen as average key size in bytes despite strings in Java are UTF-16
* encoded (and each character takes 2 bytes on-heap), because default off-heap {@link String}
* encoding is UTF-8 in {@code ChronicleSet}.)
*
* @param averageKeySize the average size in bytes of the key
* @see #constantKeySizeBySample(Object)
* @see #actualChunkSize(int)
*/
@Override
public ChronicleSetBuilder averageKeySize(double averageKeySize) {
chronicleMapBuilder.averageKeySize(averageKeySize);
return this;
}
@Override
public ChronicleSetBuilder averageKey(K averageKey) {
chronicleMapBuilder.averageKey(averageKey);
return this;
}
/**
* {@inheritDoc}
*
*
For example, if your keys are Git commit hashes:
{@code
* Set gitCommitsOfInterest = ChronicleSetBuilder.of(byte[].class)
* .constantKeySizeBySample(new byte[20])
* .create();}
*
* @see ChronicleHashBuilder#averageKeySize(double)
*/
@Override
public ChronicleSetBuilder constantKeySizeBySample(K sampleKey) {
chronicleMapBuilder.constantKeySizeBySample(sampleKey);
return this;
}
@Override
public ChronicleSetBuilder actualChunkSize(int actualChunkSize) {
chronicleMapBuilder.actualChunkSize(actualChunkSize);
return this;
}
@Override
public ChronicleSetBuilder maxChunksPerEntry(int maxChunksPerEntry) {
chronicleMapBuilder.maxChunksPerEntry(maxChunksPerEntry);
return this;
}
@Override
public ChronicleSetBuilder entries(long entries) {
chronicleMapBuilder.entries(entries);
return this;
}
@Override
public ChronicleSetBuilder maxBloatFactor(double maxBloatFactor) {
chronicleMapBuilder.maxBloatFactor(maxBloatFactor);
return this;
}
@Override
public ChronicleSetBuilder allowSegmentTiering(boolean allowSegmentTiering) {
chronicleMapBuilder.allowSegmentTiering(allowSegmentTiering);
return this;
}
@Override
public ChronicleSetBuilder nonTieredSegmentsPercentile(double nonTieredSegmentsPercentile) {
chronicleMapBuilder.nonTieredSegmentsPercentile(nonTieredSegmentsPercentile);
return this;
}
@Override
public String toString() {
return " ChronicleSetBuilder{" +
"chronicleMapBuilder=" + chronicleMapBuilder +
'}';
}
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
@Override
public boolean equals(Object o) {
return o instanceof ChronicleSetBuilder &&
chronicleMapBuilder.equals(((ChronicleSetBuilder) o).chronicleMapBuilder);
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public ChronicleSetBuilder keyReaderAndDataAccess(
SizedReader keyReader, @NotNull DataAccess keyDataAccess) {
chronicleMapBuilder.keyReaderAndDataAccess(keyReader, keyDataAccess);
return this;
}
@Override
public ChronicleSetBuilder keyMarshallers(
@NotNull BytesReader keyReader, @NotNull BytesWriter super K> keyWriter) {
chronicleMapBuilder.keyMarshallers(keyReader, keyWriter);
return this;
}
@Override
public & BytesWriter super K>>
ChronicleSetBuilder keyMarshaller(@NotNull M marshaller) {
chronicleMapBuilder.keyMarshaller(marshaller);
return this;
}
@Override
public ChronicleSetBuilder keyMarshallers(
@NotNull SizedReader keyReader, @NotNull SizedWriter super K> keyWriter) {
chronicleMapBuilder.keyMarshallers(keyReader, keyWriter);
return this;
}
@Override
public & SizedWriter super K>>
ChronicleSetBuilder keyMarshaller(@NotNull M sizedMarshaller) {
chronicleMapBuilder.keyMarshaller(sizedMarshaller);
return this;
}
@Override
public ChronicleSetBuilder keySizeMarshaller(@NotNull SizeMarshaller keySizeMarshaller) {
chronicleMapBuilder.keySizeMarshaller(keySizeMarshaller);
return this;
}
@Override
public ChronicleSetBuilder aligned64BitMemoryOperationsAtomic(
boolean aligned64BitMemoryOperationsAtomic) {
chronicleMapBuilder.aligned64BitMemoryOperationsAtomic(aligned64BitMemoryOperationsAtomic);
return this;
}
@Override
public ChronicleSetBuilder checksumEntries(boolean checksumEntries) {
chronicleMapBuilder.checksumEntries(checksumEntries);
return this;
}
/**
* Inject your SPI code around basic {@code ChronicleSet}'s operations with entries:
* removing entries and inserting new entries.
*
*
This affects behaviour of ordinary set.add(), set.remove(), calls, as well as removes
* during iterations, updates during remote calls and
* internal replication operations.
*/
public ChronicleSetBuilder entryOperations(SetEntryOperations entryOperations) {
chronicleMapBuilder.entryOperations(new MapEntryOperations() {
@Override
public Object remove(@NotNull MapEntry entry) {
//noinspection unchecked
return entryOperations.remove((SetEntry) entry);
}
@Override
public Object insert(
@NotNull MapAbsentEntry absentEntry, Data value) {
//noinspection unchecked
return entryOperations.insert((SetAbsentEntry) absentEntry);
}
});
return this;
}
@Override
public ChronicleSet create() {
final ChronicleMap map = chronicleMapBuilder.create();
return new SetFromMap<>((VanillaChronicleMap) map);
}
@Override
public ChronicleSet createPersistedTo(File file) throws IOException {
ChronicleMap map = chronicleMapBuilder.createPersistedTo(file);
return new SetFromMap<>((VanillaChronicleMap) map);
}
@Override
public ChronicleSet createOrRecoverPersistedTo(File file) throws IOException {
return createOrRecoverPersistedTo(file, true);
}
@Override
public ChronicleSet createOrRecoverPersistedTo(File file, boolean sameLibraryVersion)
throws IOException {
return createOrRecoverPersistedTo(file, sameLibraryVersion,
defaultChronicleSetCorruptionListener);
}
@Override
public ChronicleSet createOrRecoverPersistedTo(
File file, boolean sameLibraryVersion,
ChronicleHashCorruption.Listener corruptionListener) throws IOException {
ChronicleMap map = chronicleMapBuilder.createOrRecoverPersistedTo(
file, sameLibraryVersion, corruptionListener);
return new SetFromMap<>((VanillaChronicleMap) map);
}
@Override
public ChronicleSet recoverPersistedTo(File file, boolean sameBuilderConfigAndLibraryVersion)
throws IOException {
return recoverPersistedTo(file, sameBuilderConfigAndLibraryVersion,
defaultChronicleSetCorruptionListener);
}
@Override
public ChronicleSet recoverPersistedTo(
File file, boolean sameBuilderConfigAndLibraryVersion,
ChronicleHashCorruption.Listener corruptionListener) throws IOException {
ChronicleMap map = chronicleMapBuilder.recoverPersistedTo(
file, sameBuilderConfigAndLibraryVersion, corruptionListener);
return new SetFromMap<>((VanillaChronicleMap) map);
}
@Override
public ChronicleSetBuilder setPreShutdownAction(Runnable preShutdownAction) {
chronicleMapBuilder.setPreShutdownAction(preShutdownAction);
return this;
}
@Override
public ChronicleSetBuilder skipCloseOnExitHook(boolean skipCloseOnExitHook) {
chronicleMapBuilder.skipCloseOnExitHook(skipCloseOnExitHook);
return this;
}
/**
* @deprecated don't use private API in the client code
*/
@Deprecated
@Override
public Object privateAPI() {
return privateAPI;
}
}