/* * Copyright 2014, Stratio. * * 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.stratio.deep.commons.utils; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.apache.commons.lang.StringUtils; import com.stratio.deep.commons.config.BaseConfig; import com.stratio.deep.commons.config.DeepJobConfig; import com.stratio.deep.commons.config.ExtractorConfig; import com.stratio.deep.commons.entity.Cell; import com.stratio.deep.commons.entity.Cells; import com.stratio.deep.commons.entity.IDeepType; import com.stratio.deep.commons.exception.DeepExtractorInitializationException; import com.stratio.deep.commons.exception.DeepGenericException; import com.stratio.deep.commons.exception.DeepIOException; import com.stratio.deep.commons.rdd.IExtractor; import org.apache.log4j.Logger; import scala.Tuple2; /** * Utility class providing useful methods to manipulate the conversion * between ByteBuffers maps coming from the underlying Cassandra API to * instances of a concrete javabean. * * @author Luca Rosellini <luca@strat.io> */ public final class Utils { /** * The Log. */ private static transient final Logger LOG = Logger.getLogger(Utils.class); /** * Creates a new instance of the given class. * * @param <T> the type parameter * @param clazz the class object for which a new instance should be created. * @return the new instance of class clazz. */ public static <T extends IDeepType> T newTypeInstance(Class<T> clazz) { try { return clazz.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new DeepGenericException(e); } } /** * Creates a new instance of the given class name. * * @param <T> the type parameter * @param className the class object for which a new instance should be created. * @param returnClass the return class * @return the new instance of class clazz. */ @SuppressWarnings("unchecked") public static <T> T newTypeInstance(String className, Class<T> returnClass) { try { Class<T> clazz = (Class<T>) Class.forName(className); return clazz.newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { throw new DeepGenericException(e); } } /** * Quoting for working with uppercase * * @param identifier the identifier * @return the string */ public static String quote(String identifier) { if (StringUtils.isEmpty(identifier)) { return identifier; } String res = identifier.trim(); if (!res.startsWith("\"")) { res = "\"" + res; } if (!res.endsWith("\"")) { res = res + "\""; } return res; } /** * Quoting for working with uppercase * * @param identifier the identifier * @return the string */ public static String singleQuote(String identifier) { if (StringUtils.isEmpty(identifier)) { return identifier; } String res = identifier.trim(); if (!res.startsWith("'")) { res = "'" + res; } if (!res.endsWith("'")) { res = res + "'"; } return res; } /** * Returns a CQL batch query wrapping the given statements. * * @param statements the list of statements to use to generate the batch statement. * @return the batch statement. */ public static String batchQueryGenerator(List<String> statements) { StringBuilder sb = new StringBuilder("BEGIN BATCH \n"); for (String statement : statements) { sb.append(statement).append("\n"); } sb.append(" APPLY BATCH;"); return sb.toString(); } /** * Splits columns names and values as required by Datastax java driver to generate an Insert query. * * @param tuple an object containing the key Cell(s) as the first element and all the other columns as the second element. * @return an object containing an array of column names as the first element and an array of column values as the second element. */ public static Tuple2<String[], Object[]> prepareTuple4CqlDriver(Tuple2<Cells, Cells> tuple) { Cells keys = tuple._1(); Cells columns = tuple._2(); String[] names = new String[keys.size() + columns.size()]; Object[] values = new Object[keys.size() + columns.size()]; for (int k = 0; k < keys.size(); k++) { Cell cell = keys.getCellByIdx(k); names[k] = quote(cell.getCellName()); values[k] = cell.getCellValue(); } for (int v = keys.size(); v < (keys.size() + columns.size()); v++) { Cell cell = columns.getCellByIdx(v - keys.size()); names[v] = quote(cell.getCellName()); values[v] = cell.getCellValue(); } return new Tuple2<>(names, values); } /** * Resolves the setter name for the property whose name is 'propertyName' whose type is 'valueType' * in the entity bean whose class is 'entityClass'. * If we don't find a setter following Java's naming conventions, before throwing an exception we try to * resolve the setter following Scala's naming conventions. * * @param propertyName the field name of the property whose setter we want to resolve. * @param entityClass the bean class object in which we want to search for the setter. * @param valueType the class type of the object that we want to pass to the setter. * @return the resolved setter. */ @SuppressWarnings("unchecked") public static Method findSetter(String propertyName, Class entityClass, Class valueType) { Method setter; String setterName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); try { setter = entityClass.getMethod(setterName, valueType); } catch (NoSuchMethodException e) { // let's try with scala setter name try { setter = entityClass.getMethod(propertyName + "_$eq", valueType); } catch (NoSuchMethodException e1) { throw new DeepIOException(e1); } } return setter; } /** * @param object * @param fieldName * @param fieldValue * @return if the operation has been correct. */ public static boolean setFieldWithReflection(Object object, String fieldName, Object fieldValue) { Class<?> clazz = object.getClass(); while (clazz != null) { try { Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); field.set(object, fieldValue); return true; } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); } catch (Exception e) { throw new IllegalStateException(e); } } return false; } /** * Resolves the getter name for the property whose name is 'propertyName' whose type is 'valueType' * in the entity bean whose class is 'entityClass'. * If we don't find a setter following Java's naming conventions, before throwing an exception we try to * resolve the setter following Scala's naming conventions. * * @param propertyName the field name of the property whose getter we want to resolve. * @param entityClass the bean class object in which we want to search for the getter. * @return the resolved getter. */ @SuppressWarnings("unchecked") public static Method findGetter(String propertyName, Class entityClass) { Method getter; String getterName = "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); try { getter = entityClass.getMethod(getterName); } catch (NoSuchMethodException e) { // let's try with scala setter name try { getter = entityClass.getMethod(propertyName + "_$eq"); } catch (NoSuchMethodException e1) { throw new DeepIOException(e1); } } return getter; } /** * Returns the inet address for the specified location. * * @param location the address as String * @return the InetAddress object associated to the provided address. */ public static InetAddress inetAddressFromLocation(String location) { try { return InetAddress.getByName(location); } catch (UnknownHostException e) { throw new DeepIOException(e); } } /** * Return the set of fields declared at all level of class hierachy * * @param clazz the clazz * @return the field [ ] */ public static Field[] getAllFields(Class clazz) { return getAllFieldsRec(clazz, new ArrayList<Field>()); } /** * Get all fields rec. * * @param clazz the clazz * @param fields the fields * @return the field [ ] */ private static Field[] getAllFieldsRec(Class clazz, List<Field> fields) { Class superClazz = clazz.getSuperclass(); if (superClazz != null) { getAllFieldsRec(superClazz, fields); } fields.addAll(Arrays.asList(clazz.getDeclaredFields())); return fields.toArray(new Field[fields.size()]); } /** * private constructor. */ private Utils() { } /** * Remove address port. * * @param stringList the string list * @return the list */ public static List<String> removeAddressPort(List<String> stringList) { List<String> adresNoPort = new ArrayList<>(); for (String s : stringList) { int index = s.indexOf(":"); if (index > -1) { adresNoPort.add(s.substring(0, index)); continue; } adresNoPort.add(s); } return adresNoPort; } /** * Split list by comma. * * @param hosts the hosts * @return string */ public static String splitListByComma(List<String> hosts) { boolean firstHost = true; StringBuilder hostConnection = new StringBuilder(); for (String host : hosts) { if (!firstHost) { hostConnection.append(","); } hostConnection.append(host.trim()); firstHost = false; } return hostConnection.toString(); } /** * Gets extractor instance. * * @param config the config * @return the extractor instance */ public static <T, S extends BaseConfig> IExtractor<T, S> getExtractorInstance(S config) { try { Class<T> rdd = (Class<T>) config.getExtractorImplClass(); if (rdd == null) { rdd = (Class<T>) Class.forName(config.getExtractorImplClassName()); } Constructor<T> c; if (config.getEntityClass().isAssignableFrom(Cells.class)) { c = rdd.getConstructor(); return (IExtractor<T, S>) c.newInstance(); } else { c = rdd.getConstructor(Class.class); return (IExtractor<T, S>) c.newInstance(config.getEntityClass()); } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { String message = "A exception happens and we wrap with DeepExtractorInitializationException" + e.getMessage(); LOG.error(message); throw new DeepExtractorInitializationException(message,e); } } /** * Cast number type. * * @param object the object * @param clazz the clazz * @return object */ public static Object castNumberType(Object object, Class clazz) { if (Number.class.isAssignableFrom(clazz)) { // AtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short if (Double.class.isAssignableFrom(clazz)) { return ((Number) object).doubleValue(); } else if (Long.class.isAssignableFrom(clazz)) { return ((Number) object).longValue(); } else if (Float.class.isAssignableFrom(clazz)) { return ((Number) object).floatValue(); } else if (Integer.class.isAssignableFrom(clazz)) { return ((Number) object).intValue(); } else if (Short.class.isAssignableFrom(clazz)) { return ((Number) object).shortValue(); } else if (Byte.class.isAssignableFrom(clazz)) { return ((Number) object).byteValue(); } else if (BigInteger.class.isAssignableFrom(clazz)) { return BigInteger.valueOf(((Number) object).longValue()); } else if (BigDecimal.class.isAssignableFrom(clazz)) { return BigDecimal.valueOf(((Number) object).longValue()); } else if (AtomicLong.class.isAssignableFrom(clazz)) { return new AtomicLong(((Number) object).longValue()); } else if (AtomicInteger.class.isAssignableFrom(clazz)) { return new AtomicInteger(((Number) object).intValue()); } } throw new ClassCastException("it is not a Number Type" + object.getClass() + "|" + clazz); } public static Object castingUtil(String value, Class classCasting) { Object object = value; //Numeric if (Number.class.isAssignableFrom(classCasting)) { if (classCasting.isAssignableFrom(Double.class)) { return Double.valueOf(value); } else if (classCasting.isAssignableFrom(Long.class)) { return Long.valueOf(value); } else if (classCasting.isAssignableFrom(Float.class)) { return Float.valueOf(value); } else if (classCasting.isAssignableFrom(Integer.class)) { return Integer.valueOf(value); } else if (classCasting.isAssignableFrom(Short.class)) { return Short.valueOf(value); } else if (classCasting.isAssignableFrom(Byte.class)) { return Byte.valueOf(value); } } else if (String.class.isAssignableFrom(classCasting)) { return object.toString(); } //Class not recognise yet return null; } public static <S extends BaseConfig, W extends DeepJobConfig> W initConfig(S config, W deepJobConfig) { if (config instanceof ExtractorConfig) { deepJobConfig.initialize((ExtractorConfig) config); } else if (deepJobConfig.getClass().isAssignableFrom(config.getClass())) { deepJobConfig = (W) ((W) config).initialize(); } else { deepJobConfig.initialize((DeepJobConfig) config); } return deepJobConfig; } /** * Returns an instance clone. * this method gets every class property by reflection, including its parents properties * @param t * @param <T> * @return T object. */ public static <T> T cloneObjectWithParents (T t) throws IllegalAccessException, InstantiationException { T clone = (T) t.getClass().newInstance(); List<Field> allFields = new ArrayList<>(); Class parentClass = t.getClass().getSuperclass(); while (parentClass != null) { Collections.addAll(allFields, parentClass.getDeclaredFields()); parentClass = parentClass.getSuperclass(); } Collections.addAll(allFields, t.getClass().getDeclaredFields()); for (Field field : allFields) { int modifiers = field.getModifiers(); //We skip final and static fields if ((Modifier.FINAL & modifiers) != 0 || (Modifier.STATIC & modifiers) != 0) { continue; } field.setAccessible(true); Object value = field.get(t); if (Collection.class.isAssignableFrom(field.getType())) { Collection collection = (Collection) field.get(clone); if (collection == null) { collection = (Collection) field.get(t).getClass().newInstance(); } collection.addAll((Collection) field.get(t)); value = collection; } else if (Map.class.isAssignableFrom(field.getType())) { Map clonMap = (Map) field.get(t).getClass().newInstance(); clonMap.putAll((Map) field.get(t)); value = clonMap; } field.set(clone, value); } return clone; } /** * Returns an instance of ThreadPoolExecutor using an bounded queue and blocking when the worker queue is full. * @param nThreads thread pool size * @param queueSize workers queue size * @return thread pool executor */ public static ExecutorService newBlockingFixedThreadPoolExecutor(int nThreads, int queueSize) { BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(queueSize); RejectedExecutionHandler blockingRejectedExecutionHandler = new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable task, ThreadPoolExecutor executor) { try { executor.getQueue().put(task); } catch (InterruptedException e) { } } }; return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, blockingQueue, blockingRejectedExecutionHandler); } }