org.apache.calcite.adapter.geode.util.GeodeUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.calcite.adapter.geode.util.GeodeUtils.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you 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 org.apache.calcite.adapter.geode.util;

import org.apache.calcite.avatica.util.DateTimeUtils;
import org.apache.calcite.linq4j.tree.Primitive;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.util.Util;

import org.apache.commons.lang3.StringUtils;
import org.apache.geode.cache.CacheClosedException;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientCacheFactory;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.cache.query.Struct;
import org.apache.geode.pdx.PdxInstance;
import org.apache.geode.pdx.ReflectionBasedAutoSerializer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Utilities for the Geode adapter.
 */
public class GeodeUtils {

    protected static final Logger LOGGER = LoggerFactory.getLogger(GeodeUtils.class.getName());

    /**
     * Cache for the client proxy regions created in the current ClientCache.
     */
    private static final Map<String, Region> REGION_MAP = new ConcurrentHashMap<>();

    private static String currentLocatorHost = "";
    private static int currentLocatorPort = -1;

    private static final JavaTypeFactoryExtImpl JAVA_TYPE_FACTORY = new JavaTypeFactoryExtImpl();

    private GeodeUtils() {
    }

    /**
     * Creates a Geode client instance connected to locator and configured to
     * support PDX instances.
     *
     * <p>If an old instance exists, it will be destroyed and re-created.
     *
     * @param locatorHost               Locator's host address
     * @param locatorPort               Locator's port
     * @param autoSerializerPackagePath package name of the Domain classes loaded in the regions
     * @return Returns a Geode {@link ClientCache} instance connected to Geode cluster
     */
    public static synchronized ClientCache createClientCache(String locatorHost, int locatorPort,
            String autoSerializerPackagePath, boolean readSerialized) {
        if (locatorPort != currentLocatorPort || !StringUtils.equalsIgnoreCase(currentLocatorHost, locatorHost)) {
            LOGGER.info("Close existing ClientCache [" + currentLocatorHost + ":" + currentLocatorPort
                    + "] for new Locator connection at: [" + locatorHost + ":" + locatorPort + "]");
            currentLocatorHost = locatorHost;
            currentLocatorPort = locatorPort;
            closeClientCache();
        }

        try {
            // If exists returns the existing client cache. This requires that the pre-created
            // client proxy regions can also be resolved from the regionMap
            return ClientCacheFactory.getAnyInstance();
        } catch (CacheClosedException cce) {
            // Do nothing if there is no existing instance
        }

        return new ClientCacheFactory().addPoolLocator(locatorHost, locatorPort)
                .setPdxSerializer(new ReflectionBasedAutoSerializer(autoSerializerPackagePath))
                .setPdxReadSerialized(readSerialized).setPdxPersistent(false).create();
    }

    public static synchronized void closeClientCache() {
        try {
            ClientCacheFactory.getAnyInstance().close();
        } catch (CacheClosedException cce) {
            // Do nothing if there is no existing instance
        }
        REGION_MAP.clear();
    }

    /**
     * Obtains a proxy pointing to an existing Region on the server
     *
     * @param cache {@link GemFireCache} instance to interact with the Geode server
     * @param regionName  Name of the region to create proxy for.
     * @return Returns a Region proxy to a remote (on the Server) regions.
     */
    public static synchronized Region createRegion(GemFireCache cache, String regionName) {
        Objects.requireNonNull(cache, "cache");
        Objects.requireNonNull(regionName, "regionName");
        Region region = REGION_MAP.get(regionName);
        if (region == null) {
            try {
                region = ((ClientCache) cache).createClientRegionFactory(ClientRegionShortcut.PROXY)
                        .create(regionName);
            } catch (IllegalStateException e) {
                // means this is a server cache (probably part of embedded testing)
                region = cache.getRegion(regionName);
            }

            REGION_MAP.put(regionName, region);
        }

        return region;
    }

    /**
     * Converts a Geode object into a Row tuple.
     *
     * @param relDataTypeFields Table relation types
     * @param geodeResultObject Object value returned by Geode query
     * @return List of objects values corresponding to the relDataTypeFields
     */
    public static Object convertToRowValues(List<RelDataTypeField> relDataTypeFields, Object geodeResultObject) {

        Object values;

        if (geodeResultObject instanceof Struct) {
            values = handleStructEntry(relDataTypeFields, geodeResultObject);
        } else if (geodeResultObject instanceof PdxInstance) {
            values = handlePdxInstanceEntry(relDataTypeFields, geodeResultObject);
        } else {
            values = handleJavaObjectEntry(relDataTypeFields, geodeResultObject);
        }

        return values;
    }

    private static Object handleStructEntry(List<RelDataTypeField> relDataTypeFields, Object obj) {

        Struct struct = (Struct) obj;

        Object[] values = new Object[relDataTypeFields.size()];

        int index = 0;
        for (RelDataTypeField relDataTypeField : relDataTypeFields) {
            Type javaType = JAVA_TYPE_FACTORY.getJavaClass(relDataTypeField.getType());
            Object rawValue;
            try {
                rawValue = struct.get(relDataTypeField.getName());
            } catch (IllegalArgumentException e) {
                rawValue = "<error>";
                System.err.println("Could find field : " + relDataTypeField.getName());
                e.printStackTrace();
            }
            values[index++] = convert(rawValue, (Class) javaType);
        }

        if (values.length == 1) {
            return values[0];
        }

        return values;
    }

    private static Object handlePdxInstanceEntry(List<RelDataTypeField> relDataTypeFields, Object obj) {

        PdxInstance pdxEntry = (PdxInstance) obj;

        Object[] values = new Object[relDataTypeFields.size()];

        int index = 0;
        for (RelDataTypeField relDataTypeField : relDataTypeFields) {
            Type javaType = JAVA_TYPE_FACTORY.getJavaClass(relDataTypeField.getType());
            Object rawValue = pdxEntry.getField(relDataTypeField.getName());
            values[index++] = convert(rawValue, (Class) javaType);
        }

        if (values.length == 1) {
            return values[0];
        }

        return values;
    }

    private static Object handleJavaObjectEntry(List<RelDataTypeField> relDataTypeFields, Object obj) {

        Class<?> clazz = obj.getClass();
        if (relDataTypeFields.size() == 1) {
            try {
                Field javaField = clazz.getDeclaredField(relDataTypeFields.get(0).getName());
                javaField.setAccessible(true);
                return javaField.get(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        Object[] values = new Object[relDataTypeFields.size()];

        int index = 0;
        for (RelDataTypeField relDataTypeField : relDataTypeFields) {
            try {
                Field javaField = clazz.getDeclaredField(relDataTypeField.getName());
                javaField.setAccessible(true);
                values[index++] = javaField.get(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return values;
    }

    private static Object convert(Object o, Class clazz) {
        if (o == null) {
            return null;
        }
        Primitive primitive = Primitive.of(clazz);
        if (primitive != null) {
            clazz = primitive.boxClass;
        } else {
            primitive = Primitive.ofBox(clazz);
        }
        if (clazz == null) {
            // This is in case of nested Objects!
            if (o instanceof PdxInstance) {
                return Util.toString(((PdxInstance) o).getFieldNames(), "PDX[", ",", "]");
            }
            return o.toString();
        }
        if (clazz.isInstance(o)) {
            return o;
        }
        if (o instanceof Date && primitive != null) {
            o = ((Date) o).getTime() / DateTimeUtils.MILLIS_PER_DAY;
        }
        if (o instanceof Number && primitive != null) {
            return primitive.number((Number) o);
        }
        return o;
    }

    /**
     * Extract the first entity of each Regions and use it to build a table types.
     *
     * @param region existing region
     * @return derived data type.
     */
    public static RelDataType autodetectRelTypeFromRegion(Region<?, ?> region) {
        Objects.requireNonNull(region, "region");

        // try to detect type using value constraints (if they exists)
        final Class<?> constraint = region.getAttributes().getValueConstraint();
        if (constraint != null && !PdxInstance.class.isAssignableFrom(constraint)) {
            return new JavaTypeFactoryExtImpl().createStructType(constraint);
        }

        final Iterator<?> iter;
        if (region.getAttributes().getPoolName() == null) {
            // means current cache is server (not ClientCache)
            iter = region.keySet().iterator();
        } else {
            // for ClientCache
            iter = region.keySetOnServer().iterator();
        }

        if (!iter.hasNext()) {
            String message = String.format(Locale.ROOT, "Region %s is empty, can't " + "autodetect type(s)",
                    region.getName());
            throw new IllegalStateException(message);
        }

        final Object entry = region.get(iter.next());
        return createRelDataType(entry);
    }

    // Create Relational Type by inferring a Geode entry or response instance.
    private static RelDataType createRelDataType(Object regionEntry) {
        JavaTypeFactoryExtImpl typeFactory = new JavaTypeFactoryExtImpl();
        if (regionEntry instanceof PdxInstance) {
            return typeFactory.createPdxType((PdxInstance) regionEntry);
        } else {
            return typeFactory.createStructType(regionEntry.getClass());
        }
    }

}

// End GeodeUtils.java