/* * Copyright (c) 2008-2017, 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.MapAttributeConfig; import com.hazelcast.internal.serialization.InternalSerializationService; import com.hazelcast.nio.serialization.Data; 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 static com.hazelcast.query.impl.getters.ExtractorHelper.extractArgumentsFromAttributeName; import static com.hazelcast.query.impl.getters.ExtractorHelper.extractAttributeNameNameWithoutArguments; // one instance per MapContainer public final class Extractors { private static final int MAX_CLASSES_IN_CACHE = 1000; private static final int MAX_GETTERS_PER_CLASS_IN_CACHE = 100; private static final float EVICTION_PERCENTAGE = 0.2f; private volatile PortableGetter genericPortableGetter; /** * 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<String, ValueExtractor> extractors; private final EvictableGetterCache getterCache; private final DefaultArgumentParser argumentsParser; // TODO InternalSerializationService should be passed in constructor public Extractors(List<MapAttributeConfig> mapAttributeConfigs, ClassLoader classLoader) { this.extractors = ExtractorHelper.instantiateExtractors(mapAttributeConfigs, classLoader); this.getterCache = new EvictableGetterCache(MAX_CLASSES_IN_CACHE, MAX_GETTERS_PER_CLASS_IN_CACHE, EVICTION_PERCENTAGE); this.argumentsParser = new DefaultArgumentParser(); } public Object extract(InternalSerializationService serializationService, Object target, String attributeName) { Object targetObject = getTargetObject(serializationService, target); if (targetObject != null) { Getter getter = getGetter(serializationService, targetObject, attributeName); try { return getter.getValue(targetObject, attributeName); } catch (Exception ex) { throw new QueryException(ex); } } return null; } /** * @return Data (in this case it's portable) or Object (in this case it's non-portable) */ private static Object getTargetObject(InternalSerializationService serializationService, Object target) { Data targetData; if (target instanceof Portable) { targetData = serializationService.toData(target); if (targetData.isPortable()) { return targetData; } } if (target instanceof Data) { targetData = (Data) target; if (targetData.isPortable()) { return targetData; } else { // convert non-portable Data to object return serializationService.toObject(target); } } return target; } Getter getGetter(InternalSerializationService serializationService, Object targetObject, String attributeName) { Getter getter = getterCache.getGetter(targetObject.getClass(), attributeName); if (getter == null) { getter = instantiateGetter(serializationService, targetObject, attributeName); if (getter.isCacheable()) { getterCache.putGetter(targetObject.getClass(), attributeName, getter); } } return getter; } private Getter instantiateGetter(InternalSerializationService serializationService, Object targetObject, String attributeName) { String attributeNameWithoutArguments = extractAttributeNameNameWithoutArguments(attributeName); ValueExtractor valueExtractor = extractors.get(attributeNameWithoutArguments); if (valueExtractor != null) { Object arguments = argumentsParser.parse(extractArgumentsFromAttributeName(attributeName)); return new ExtractorGetter(serializationService, valueExtractor, arguments); } else { if (targetObject instanceof Data) { if (genericPortableGetter == null) { // will be initialised a couple of times in the worst case genericPortableGetter = new PortableGetter(serializationService); } return genericPortableGetter; } else { return ReflectionHelper.createGetter(targetObject, attributeName); } } } public static Extractors empty() { return new Extractors(Collections.<MapAttributeConfig>emptyList(), null); } }