/******************************************************************************* * * Pentaho Big Data * * Copyright (C) 2002-2013 by Pentaho : http://www.pentaho.com * ******************************************************************************* * * 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 org.pentaho.hadoop.shim.common; import org.apache.sqoop.Sqoop; import org.pentaho.hadoop.shim.HadoopConfigurationClassLoader; import org.pentaho.hadoop.shim.api.Configuration; import java.util.concurrent.Callable; /** * Sqoop shim to handle working around Sqoop's ConfigurationManager and how it builds up the class path when compiling * temporary files. * <p/> * <p> This applies to any version of Hadoop that bundles classes in separate jars instead of the conglomerate "core" * jar. </p> */ public class ClassPathModifyingSqoopShim extends CommonSqoopShim { protected static final String PROPERTY_JAVA_CLASS_PATH = "java.class.path"; /** * Run a given {@link java.util.concurrent.Callable} within a code block that sets the {@code "java.class.path"} property to the path * defined for the {@link org.pentaho.hadoop.shim.HadoopConfigurationClassLoader} used to load this class, if it was used. If not, this is the * same as calling {@code callable.call()}. * * @param callable Callable to execute with a modified class path set. * @return */ public int runWithModifiedClassPathProperty( Callable<Integer> callable ) { /** * WARNING: This is non-thread safe. This is only to work around the * deficiencies of the Sqoop CompilationManager (as of 1.4.1) as it will * only look for the Hadoop "core" jar. For CDH4 the necessary classes are contained within a * "common" jar. */ String newClassPath = getClassPathString(); String originalClassPath = System.getProperty( PROPERTY_JAVA_CLASS_PATH ); if ( newClassPath != null ) { System.setProperty( PROPERTY_JAVA_CLASS_PATH, newClassPath ); } try { Integer returnVal = callable.call(); return returnVal == null ? Integer.MIN_VALUE : returnVal.intValue(); } catch ( Exception ex ) { throw new RuntimeException( ex ); } finally { if ( originalClassPath != null ) { System.setProperty( PROPERTY_JAVA_CLASS_PATH, originalClassPath ); } } } @Override public int runTool( final String[] args, final Configuration c ) { return runWithModifiedClassPathProperty( new Callable<Integer>() { @Override public Integer call() throws Exception { ClassLoader cl = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader( getClass().getClassLoader() ); try { return Sqoop.runTool( args, ShimUtils.asConfiguration( c ) ); } finally { Thread.currentThread().setContextClassLoader( cl ); } } } ); } /** * @return the class path to set for each run of {@link #runWithModifiedClassPathProperty(java.util.concurrent.Callable)}. */ protected String getClassPathString() { ClassLoader cl = getClass().getClassLoader(); if ( HadoopConfigurationClassLoader.class.isAssignableFrom( cl.getClass() ) ) { HadoopConfigurationClassLoader hccl = (HadoopConfigurationClassLoader) cl; return hccl.generateClassPathString(); } return null; } }