package mil.nga.giat.geowave.mapreduce; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.mapreduce.JobContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import mil.nga.giat.geowave.core.index.ByteArrayId; import mil.nga.giat.geowave.core.index.ByteArrayUtils; import mil.nga.giat.geowave.core.index.PersistenceUtils; import mil.nga.giat.geowave.core.store.AdapterToIndexMapping; import mil.nga.giat.geowave.core.store.DataStore; import mil.nga.giat.geowave.core.store.GeoWaveStoreFinder; import mil.nga.giat.geowave.core.store.adapter.AdapterIndexMappingStore; import mil.nga.giat.geowave.core.store.adapter.AdapterStore; import mil.nga.giat.geowave.core.store.adapter.DataAdapter; import mil.nga.giat.geowave.core.store.adapter.statistics.DataStatisticsStore; import mil.nga.giat.geowave.core.store.index.IndexStore; import mil.nga.giat.geowave.core.store.index.PrimaryIndex; import mil.nga.giat.geowave.core.store.operations.remote.options.DataStorePluginOptions; /** * This class forms the basis for GeoWave input and output format configuration. */ public class GeoWaveConfiguratorBase { protected static final Logger LOGGER = LoggerFactory.getLogger(GeoWaveConfiguratorBase.class); private static final String KEY_SEPARATOR = "-"; public static enum GeoWaveConfg { INDEX, DATA_ADAPTER, ADAPTER_TO_INDEX, STORE_CONFIG_OPTION } /** * Provides a configuration key for a given feature enum, prefixed by the * implementingClass, and suffixed by a custom String * * @param implementingClass * the class whose name will be used as a prefix for the property * configuration key * @param e * the enum used to provide the unique part of the configuration * key * * @param suffix * the custom suffix to be used in the configuration key * @return the configuration key */ public static String enumToConfKey( final Class<?> implementingClass, final Enum<?> e, final String suffix ) { return enumToConfKey( implementingClass, e) + KEY_SEPARATOR + suffix; } /** * Provides a configuration key for a given feature enum, prefixed by the * implementingClass * * @param implementingClass * the class whose name will be used as a prefix for the property * configuration key * @param e * the enum used to provide the unique part of the configuration * key * @return the configuration key */ public static String enumToConfKey( final Class<?> implementingClass, final Enum<?> e ) { final String s = implementingClass.getSimpleName() + "." + e.getDeclaringClass().getSimpleName() + "." + org.apache.hadoop.util.StringUtils.camelize(e.name().toLowerCase( Locale.ENGLISH)); return s; } public static final <T> T getInstance( final Class<?> implementingClass, final Enum<?> e, final JobContext context, final Class<T> interfaceClass ) throws InstantiationException, IllegalAccessException { return (T) getConfiguration( context).getClass( enumToConfKey( implementingClass, e), interfaceClass).newInstance(); } public static final <T> T getInstance( final Class<?> implementingClass, final Enum<?> e, final JobContext context, final Class<T> interfaceClass, final Class<? extends T> defaultClass ) throws InstantiationException, IllegalAccessException { return getConfiguration( context).getClass( enumToConfKey( implementingClass, e), defaultClass, interfaceClass).newInstance(); } public static DataStore getDataStore( final Class<?> implementingClass, final JobContext context ) { return GeoWaveStoreFinder.createDataStore(getStoreOptionsMap( implementingClass, context)); } public static DataStatisticsStore getDataStatisticsStore( final Class<?> implementingClass, final JobContext context ) { return GeoWaveStoreFinder.createDataStatisticsStore(getStoreOptionsMap( implementingClass, context)); } public static void setStoreOptionsMap( final Class<?> implementingClass, final Configuration config, final Map<String, String> dataStoreOptions ) { if ((dataStoreOptions != null) && !dataStoreOptions.isEmpty()) { for (final Entry<String, String> entry : dataStoreOptions.entrySet()) { config.set( enumToConfKey( implementingClass, GeoWaveConfg.STORE_CONFIG_OPTION, entry.getKey()), entry.getValue()); } } else { final Map<String, String> existingVals = config.getValByRegex(enumToConfKey( implementingClass, GeoWaveConfg.STORE_CONFIG_OPTION) + "*"); for (final String k : existingVals.keySet()) { config.unset(k); } } } public static DataStorePluginOptions getStoreOptions( final Class<?> implementingClass, final JobContext context ) { final Map<String, String> options = getStoreOptionsMapInternal( implementingClass, getConfiguration(context)); try { return new DataStorePluginOptions( options); } catch (final IllegalArgumentException e) { LOGGER.warn( "Unable to get data store options from job context", e); return null; } } public static Map<String, String> getStoreOptionsMap( final Class<?> implementingClass, final JobContext context ) { return getStoreOptionsMapInternal( implementingClass, getConfiguration(context)); } public static void addIndex( final Class<?> implementingClass, final Configuration config, final PrimaryIndex index ) { if (index != null) { config.set( enumToConfKey( implementingClass, GeoWaveConfg.INDEX, index.getId().getString()), ByteArrayUtils.byteArrayToString(PersistenceUtils.toBinary(index))); } } public static PrimaryIndex getIndex( final Class<?> implementingClass, final JobContext context, final ByteArrayId indexId ) { return getIndexInternal( implementingClass, getConfiguration(context), indexId); } public static void addAdapterToIndexMapping( final Class<?> implementingClass, final Configuration conf, final AdapterToIndexMapping adapterToIndexMapping ) { if (adapterToIndexMapping != null) { conf.set( enumToConfKey( implementingClass, GeoWaveConfg.ADAPTER_TO_INDEX, adapterToIndexMapping.getAdapterId().getString()), ByteArrayUtils.byteArrayToString(PersistenceUtils.toBinary(adapterToIndexMapping))); } } public static AdapterToIndexMapping getAdapterToIndexMapping( final Class<?> implementingClass, final JobContext context, final ByteArrayId adapterId ) { return getAdapterToIndexMappingInternal( implementingClass, getConfiguration(context), adapterId); } private static AdapterToIndexMapping getAdapterToIndexMappingInternal( final Class<?> implementingClass, final Configuration configuration, final ByteArrayId adapterId ) { final String input = configuration.get(enumToConfKey( implementingClass, GeoWaveConfg.ADAPTER_TO_INDEX, adapterId.getString())); if (input != null) { final byte[] dataAdapterBytes = ByteArrayUtils.byteArrayFromString(input); return PersistenceUtils.fromBinary( dataAdapterBytes, AdapterToIndexMapping.class); } return null; } public static void addDataAdapter( final Class<?> implementingClass, final Configuration conf, final DataAdapter<?> adapter ) { if (adapter != null) { conf.set( enumToConfKey( implementingClass, GeoWaveConfg.DATA_ADAPTER, adapter.getAdapterId().getString()), ByteArrayUtils.byteArrayToString(PersistenceUtils.toBinary(adapter))); } } public static DataAdapter<?> getDataAdapter( final Class<?> implementingClass, final JobContext context, final ByteArrayId adapterId ) { return getDataAdapterInternal( implementingClass, getConfiguration(context), adapterId); } private static DataAdapter<?> getDataAdapterInternal( final Class<?> implementingClass, final Configuration configuration, final ByteArrayId adapterId ) { final String input = configuration.get(enumToConfKey( implementingClass, GeoWaveConfg.DATA_ADAPTER, adapterId.getString())); if (input != null) { final byte[] dataAdapterBytes = ByteArrayUtils.byteArrayFromString(input); return PersistenceUtils.fromBinary( dataAdapterBytes, DataAdapter.class); } return null; } public static DataAdapter<?>[] getDataAdapters( final Class<?> implementingClass, final JobContext context ) { return getDataAdaptersInternal( implementingClass, getConfiguration(context)); } private static Map<String, String> getStoreOptionsMapInternal( final Class<?> implementingClass, final Configuration configuration ) { final String prefix = enumToConfKey( implementingClass, GeoWaveConfg.STORE_CONFIG_OPTION) + KEY_SEPARATOR; final Map<String, String> enumMap = configuration.getValByRegex(prefix + "*"); final Map<String, String> retVal = new HashMap<String, String>(); for (final Entry<String, String> entry : enumMap.entrySet()) { final String key = entry.getKey(); retVal.put( key.substring(prefix.length()), entry.getValue()); } return retVal; } private static DataAdapter<?>[] getDataAdaptersInternal( final Class<?> implementingClass, final Configuration configuration ) { final Map<String, String> input = configuration.getValByRegex(enumToConfKey( implementingClass, GeoWaveConfg.DATA_ADAPTER) + "*"); if (input != null) { final List<DataAdapter<?>> adapters = new ArrayList<DataAdapter<?>>( input.size()); for (final String dataAdapterStr : input.values()) { final byte[] dataAdapterBytes = ByteArrayUtils.byteArrayFromString(dataAdapterStr); adapters.add(PersistenceUtils.fromBinary( dataAdapterBytes, DataAdapter.class)); } return adapters.toArray(new DataAdapter[adapters.size()]); } return new DataAdapter[] {}; } private static PrimaryIndex getIndexInternal( final Class<?> implementingClass, final Configuration configuration, final ByteArrayId indexId ) { final String input = configuration.get(enumToConfKey( implementingClass, GeoWaveConfg.INDEX, indexId.getString())); if (input != null) { final byte[] indexBytes = ByteArrayUtils.byteArrayFromString(input); return PersistenceUtils.fromBinary( indexBytes, PrimaryIndex.class); } return null; } public static PrimaryIndex[] getIndices( final Class<?> implementingClass, final JobContext context ) { return getIndicesInternal( implementingClass, getConfiguration(context)); } public static IndexStore getJobContextIndexStore( final Class<?> implementingClass, final JobContext context ) { final Map<String, String> configOptions = getStoreOptionsMap( implementingClass, context); return new JobContextIndexStore( context, GeoWaveStoreFinder.createIndexStore(configOptions)); } public static AdapterStore getJobContextAdapterStore( final Class<?> implementingClass, final JobContext context ) { final Map<String, String> configOptions = getStoreOptionsMap( implementingClass, context); return new JobContextAdapterStore( context, GeoWaveStoreFinder.createAdapterStore(configOptions)); } public static AdapterIndexMappingStore getJobContextAdapterIndexMappingStore( final Class<?> implementingClass, final JobContext context ) { final Map<String, String> configOptions = getStoreOptionsMap( implementingClass, context); return new JobContextAdapterIndexMappingStore( context, GeoWaveStoreFinder.createAdapterIndexMappingStore(configOptions)); } private static PrimaryIndex[] getIndicesInternal( final Class<?> implementingClass, final Configuration configuration ) { final Map<String, String> input = configuration.getValByRegex(enumToConfKey( implementingClass, GeoWaveConfg.INDEX) + "*"); if (input != null) { final List<PrimaryIndex> indices = new ArrayList<PrimaryIndex>( input.size()); for (final String indexStr : input.values()) { final byte[] indexBytes = ByteArrayUtils.byteArrayFromString(indexStr); indices.add(PersistenceUtils.fromBinary( indexBytes, PrimaryIndex.class)); } return indices.toArray(new PrimaryIndex[indices.size()]); } return new PrimaryIndex[] {}; } // use reflection to pull the Configuration out of the JobContext for Hadoop // 1 and Hadoop 2 compatibility public static Configuration getConfiguration( final JobContext context ) { try { final Class<?> c = GeoWaveConfiguratorBase.class.getClassLoader().loadClass( "org.apache.hadoop.mapreduce.JobContext"); final Method m = c.getMethod("getConfiguration"); final Object o = m.invoke( context, new Object[0]); return (Configuration) o; } catch (final Exception e) { throw new RuntimeException( e); } } public static void setRemoteInvocationParams( final String hdfsHostPort, final String jobTrackerOrResourceManagerHostPort, final Configuration conf ) { conf.set( "fs.defaultFS", hdfsHostPort); conf.set( "fs.hdfs.impl", org.apache.hadoop.hdfs.DistributedFileSystem.class.getName()); // if this property is used, it hadoop does not support yarn conf.set( "mapred.job.tracker", jobTrackerOrResourceManagerHostPort); // the following 3 properties will only be used if the hadoop version // does support yarn if ("local".equals(jobTrackerOrResourceManagerHostPort)) { conf.set( "mapreduce.framework.name", "local"); } else { conf.set( "mapreduce.framework.name", "yarn"); } conf.set( "yarn.resourcemanager.address", jobTrackerOrResourceManagerHostPort); // if remotely submitted with yarn, the job configuration xml will be // written to this staging directory, it is generally good practice to // ensure the staging directory is different for each user String user = System.getProperty("user.name"); if ((user == null) || user.isEmpty()) { user = "default"; } conf.set( "yarn.app.mapreduce.am.staging-dir", "/tmp/hadoop-" + user); } }