
com.hazelcast.query.impl.getters.Extractors Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
*
* 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 com.hazelcast.query.impl.getters;
import com.hazelcast.config.AttributeConfig;
import com.hazelcast.core.HazelcastJsonValue;
import com.hazelcast.internal.namespace.NamespaceUtil;
import com.hazelcast.internal.serialization.Data;
import com.hazelcast.internal.serialization.InternalSerializationService;
import com.hazelcast.internal.serialization.impl.compact.CompactGenericRecord;
import com.hazelcast.internal.serialization.impl.portable.PortableGenericRecord;
import com.hazelcast.internal.util.Preconditions;
import com.hazelcast.nio.serialization.HazelcastSerializationException;
import com.hazelcast.nio.serialization.Portable;
import com.hazelcast.query.QueryException;
import com.hazelcast.query.extractor.ValueExtractor;
import com.hazelcast.query.impl.DefaultArgumentParser;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import static com.hazelcast.query.impl.getters.ExtractorHelper.extractArgumentsFromAttributeName;
import static com.hazelcast.query.impl.getters.ExtractorHelper.extractAttributeNameNameWithoutArguments;
import static com.hazelcast.query.impl.getters.ExtractorHelper.instantiateExtractors;
import static com.hazelcast.query.impl.getters.GetterCache.EVICTABLE_GETTER_CACHE_SUPPLIER;
// one instance per MapContainer
public final class Extractors {
final GetterCache getterCache;
private volatile PortableGetter portableGetter;
private volatile JsonDataGetter jsonDataGetter;
private volatile CompactGetter compactGetter;
/**
* Maps the extractorAttributeName WITHOUT the arguments to a
* ValueExtractor instance. The name does not contain the argument
* since it's not allowed to register an extractor under an
* attribute name that contains an argument in square brackets.
*/
private final Map extractors;
private final InternalSerializationService ss;
private final DefaultArgumentParser argumentsParser;
private Extractors(
List attributeConfigs,
ClassLoader classLoader,
InternalSerializationService ss,
Supplier getterCacheSupplier
) {
this.extractors = attributeConfigs == null
? Collections.emptyMap()
: instantiateExtractors(attributeConfigs, classLoader);
this.getterCache = getterCacheSupplier.get();
this.argumentsParser = new DefaultArgumentParser();
this.ss = ss;
}
public Object extract(Object target, String attributeName, Object metadata) {
return extract(target, attributeName, metadata, true);
}
public Object extract(Object target, String attributeName, Object metadata,
boolean failOnMissingReflectiveAttribute) {
Object targetObject = getTargetObject(target);
if (targetObject != null) {
Getter getter = getGetter(targetObject, attributeName, failOnMissingReflectiveAttribute);
try {
// For CompactGetter and PortableGetter metadata is a boolean
// indicating whether lazy deserialization should be used or not.
return NamespaceUtil.callWithOwnClassLoader(getter,
() -> getter.getValue(targetObject, attributeName, metadata));
} catch (Exception ex) {
throw new QueryException(ex);
}
}
return null;
}
/**
* Returns the form of this data that is queryable.
* Returns {@link Data} if {@code target} is
*
* - a portable object either in Data form or Object form
* - a {@link HazelcastJsonValue} in Data form
*
* Otherwise, returns object form.
*
* @return Data or Object
*/
private Object getTargetObject(Object target) {
Data targetData;
if (target instanceof Portable) {
targetData = ss.toData(target);
if (targetData.isPortable()) {
return targetData;
}
}
if (target instanceof Data data) {
targetData = data;
if (targetData.isPortable() || targetData.isJson() || targetData.isCompact()) {
return targetData;
} else {
// convert non-portable Data to object
return ss.toObject(target);
}
}
return target;
}
Getter getGetter(Object targetObject, String attributeName, boolean failOnMissingReflectiveAttribute) {
Getter getter = getterCache.getGetter(targetObject.getClass(), attributeName);
if (getter == null) {
getter = instantiateGetter(targetObject, attributeName, failOnMissingReflectiveAttribute);
if (getter.isCacheable()) {
getterCache.putGetter(targetObject.getClass(), attributeName, getter);
}
}
return getter;
}
private Getter instantiateGetter(Object targetObject, String attributeName, boolean failOnMissingReflectiveAttribute) {
String attributeNameWithoutArguments = extractAttributeNameNameWithoutArguments(attributeName);
ValueExtractor valueExtractor = extractors.get(attributeNameWithoutArguments);
if (valueExtractor != null) {
Object arguments = argumentsParser.parse(extractArgumentsFromAttributeName(attributeName));
return new ExtractorGetter(ss, valueExtractor, arguments);
} else if (targetObject instanceof Data data) {
return instantiateGetterForData(data);
} else if (targetObject instanceof HazelcastJsonValue) {
return JsonGetter.INSTANCE;
} else if (targetObject instanceof PortableGenericRecord) {
if (portableGetter == null) {
// will be initialised a couple of times in the worst case
portableGetter = new PortableGetter(ss);
}
return portableGetter;
} else if (targetObject instanceof CompactGenericRecord) {
if (compactGetter == null) {
// will be initialised a couple of times in the worst case
compactGetter = new CompactGetter(ss);
}
return compactGetter;
} else {
return ReflectionHelper.createGetter(targetObject, attributeName, failOnMissingReflectiveAttribute);
}
}
private Getter instantiateGetterForData(Data data) {
if (data.isPortable()) {
if (portableGetter == null) {
// will be initialised a couple of times in the worst case
portableGetter = new PortableGetter(ss);
}
return portableGetter;
}
if (data.isJson()) {
if (jsonDataGetter == null) {
// will be initialised a couple of times in the worst case
jsonDataGetter = new JsonDataGetter(ss);
}
return jsonDataGetter;
}
if (data.isCompact()) {
if (compactGetter == null) {
// will be initialised a couple of times in the worst case
compactGetter = new CompactGetter(ss);
}
return compactGetter;
}
throw new HazelcastSerializationException("No Data getter found for type " + data.getType());
}
public static Extractors.Builder newBuilder(InternalSerializationService ss) {
return new Extractors.Builder(ss);
}
/**
* Builder which is used to create a new Extractors object.
*/
public static final class Builder {
private ClassLoader classLoader;
private List attributeConfigs;
private Supplier getterCacheSupplier = EVICTABLE_GETTER_CACHE_SUPPLIER;
private final InternalSerializationService ss;
public Builder(InternalSerializationService ss) {
this.ss = Preconditions.checkNotNull(ss);
}
public Builder setGetterCacheSupplier(Supplier getterCacheSupplier) {
this.getterCacheSupplier = getterCacheSupplier;
return this;
}
public Builder setAttributeConfigs(List attributeConfigs) {
this.attributeConfigs = attributeConfigs;
return this;
}
public Builder setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
return this;
}
/**
* @return a new instance of Extractors
*/
public Extractors build() {
return new Extractors(attributeConfigs, classLoader, ss, getterCacheSupplier);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy