    001    /*
    002     * Copyright (c) 2007-2014 Concurrent, Inc. All Rights Reserved.
    003     *
    004     * Project and contact information: http://www.cascading.org/
    005     *
    006     * This file is part of the Cascading project.
    007     *
    008     * Licensed under the Apache License, Version 2.0 (the "License");
    009     * you may not use this file except in compliance with the License.
    010     * You may obtain a copy of the License at
    011     *
    012     *     http://www.apache.org/licenses/LICENSE-2.0
    013     *
    014     * Unless required by applicable law or agreed to in writing, software
    015     * distributed under the License is distributed on an "AS IS" BASIS,
    016     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    017     * See the License for the specific language governing permissions and
    018     * limitations under the License.
    019     */
    020
    021    package cascading.util;
    022
    023    import java.beans.Expression;
    024    import java.io.FileWriter;
    025    import java.io.IOException;
    026    import java.io.PrintStream;
    027    import java.io.PrintWriter;
    028    import java.io.StringWriter;
    029    import java.io.Writer;
    030    import java.lang.reflect.Constructor;
    031    import java.lang.reflect.Field;
    032    import java.lang.reflect.Method;
    033    import java.lang.reflect.Type;
    034    import java.security.MessageDigest;
    035    import java.security.NoSuchAlgorithmException;
    036    import java.util.ArrayList;
    037    import java.util.Arrays;
    038    import java.util.Collection;
    039    import java.util.Collections;
    040    import java.util.HashSet;
    041    import java.util.Iterator;
    042    import java.util.LinkedHashSet;
    043    import java.util.List;
    044    import java.util.Set;
    045    import java.util.UUID;
    046
    047    import cascading.CascadingException;
    048    import cascading.flow.FlowElement;
    049    import cascading.flow.FlowException;
    050    import cascading.flow.planner.Scope;
    051    import cascading.operation.Operation;
    052    import cascading.pipe.Pipe;
    053    import cascading.scheme.Scheme;
    054    import cascading.tap.MultiSourceTap;
    055    import cascading.tap.Tap;
    056    import org.jgrapht.ext.DOTExporter;
    057    import org.jgrapht.ext.EdgeNameProvider;
    058    import org.jgrapht.ext.IntegerNameProvider;
    059    import org.jgrapht.ext.MatrixExporter;
    060    import org.jgrapht.ext.VertexNameProvider;
    061    import org.jgrapht.graph.SimpleDirectedGraph;
    062    import org.slf4j.Logger;
    063    import org.slf4j.LoggerFactory;
    064
    065    /** Class Util provides reusable operations. */
    066    public class Util
    067      {
    068      public static int ID_LENGTH = 32;
    069
    070      private static final Logger LOG = LoggerFactory.getLogger( Util.class );
    071      private static final String HEXES = "0123456789ABCDEF";
    072
    073      public static synchronized String createUniqueID()
    074        {
    075        // creates a cryptographically secure random value
    076        String value = UUID.randomUUID().toString();
    077        return value.toUpperCase().replaceAll( "-", "" );
    078        }
    079
    080      public static String createID( String rawID )
    081        {
    082        return createID( rawID.getBytes() );
    083        }
    084
    085      /**
    086       * Method CreateID returns a HEX hash of the given bytes with length 32 characters long.
    087       *
    088       * @param bytes the bytes
    089       * @return string
    090       */
    091      public static String createID( byte[] bytes )
    092        {
    093        try
    094          {
    095          return getHex( MessageDigest.getInstance( "MD5" ).digest( bytes ) );
    096          }
    097        catch( NoSuchAlgorithmException exception )
    098          {
    099          throw new RuntimeException( "unable to digest string" );
    100          }
    101        }
    102
    103      private static String getHex( byte[] bytes )
    104        {
    105        if( bytes == null )
    106          return null;
    107
    108        final StringBuilder hex = new StringBuilder( 2 * bytes.length );
    109
    110        for( final byte b : bytes )
    111          hex.append( HEXES.charAt( ( b & 0xF0 ) >> 4 ) ).append( HEXES.charAt( b & 0x0F ) );
    112
    113        return hex.toString();
    114        }
    115
    116      public static <T> T[] copy( T[] source )
    117        {
    118        if( source == null )
    119          return null;
    120
    121        return Arrays.copyOf( source, source.length );
    122        }
    123
    124      public static String unique( String value, String delim )
    125        {
    126        String[] split = value.split( delim );
    127
    128        Set<String> values = new LinkedHashSet<String>();
    129
    130        Collections.addAll( values, split );
    131
    132        return join( values, delim );
    133        }
    134
    135      /**
    136       * This method joins the values in the given list with the delim String value.
    137       *
    138       * @param list
    139       * @param delim
    140       * @return String
    141       */
    142      public static String join( int[] list, String delim )
    143        {
    144        return join( list, delim, false );
    145        }
    146
    147      public static String join( int[] list, String delim, boolean printNull )
    148        {
    149        StringBuffer buffer = new StringBuffer();
    150        int count = 0;
    151
    152        for( Object s : list )
    153          {
    154          if( count != 0 )
    155            buffer.append( delim );
    156
    157          if( printNull || s != null )
    158            buffer.append( s );
    159
    160          count++;
    161          }
    162
    163        return buffer.toString();
    164        }
    165
    166      public static String join( String delim, String... strings )
    167        {
    168        return join( delim, false, strings );
    169        }
    170
    171      public static String join( String delim, boolean printNull, String... strings )
    172        {
    173        return join( strings, delim, printNull );
    174        }
    175
    176      /**
    177       * This method joins the values in the given list with the delim String value.
    178       *
    179       * @param list
    180       * @param delim
    181       * @return a String
    182       */
    183      public static String join( Object[] list, String delim )
    184        {
    185        return join( list, delim, false );
    186        }
    187
    188      public static String join( Object[] list, String delim, boolean printNull )
    189        {
    190        return join( list, delim, printNull, 0 );
    191        }
    192
    193      public static String join( Object[] list, String delim, boolean printNull, int beginAt )
    194        {
    195        return join( list, delim, printNull, beginAt, list.length - beginAt );
    196        }
    197
    198      public static String join( Object[] list, String delim, boolean printNull, int beginAt, int length )
    199        {
    200        StringBuffer buffer = new StringBuffer();
    201        int count = 0;
    202
    203        for( int i = beginAt; i < beginAt + length; i++ )
    204          {
    205          Object s = list[ i ];
    206          if( count != 0 )
    207            buffer.append( delim );
    208
    209          if( printNull || s != null )
    210            buffer.append( s );
    211
    212          count++;
    213          }
    214
    215        return buffer.toString();
    216        }
    217
    218      public static String join( Iterable iterable, String delim, boolean printNull )
    219        {
    220        int count = 0;
    221
    222        StringBuilder buffer = new StringBuilder();
    223
    224        for( Object s : iterable )
    225          {
    226          if( count != 0 )
    227            buffer.append( delim );
    228
    229          if( printNull || s != null )
    230            buffer.append( s );
    231
    232          count++;
    233          }
    234
    235        return buffer.toString();
    236        }
    237
    238      /**
    239       * This method joins each value in the collection with a tab character as the delimiter.
    240       *
    241       * @param collection
    242       * @return a String
    243       */
    244      public static String join( Collection collection )
    245        {
    246        return join( collection, "\t" );
    247        }
    248
    249      /**
    250       * This method joins each valuein the collection with the given delimiter.
    251       *
    252       * @param collection
    253       * @param delim
    254       * @return a String
    255       */
    256      public static String join( Collection collection, String delim )
    257        {
    258        return join( collection, delim, false );
    259        }
    260
    261      public static String join( Collection collection, String delim, boolean printNull )
    262        {
    263        StringBuffer buffer = new StringBuffer();
    264
    265        join( buffer, collection, delim, printNull );
    266
    267        return buffer.toString();
    268        }
    269
    270      /**
    271       * This method joins each value in the collection with the given delimiter. All results are appended to the
    272       * given {@link StringBuffer} instance.
    273       *
    274       * @param buffer
    275       * @param collection
    276       * @param delim
    277       */
    278      public static void join( StringBuffer buffer, Collection collection, String delim )
    279        {
    280        join( buffer, collection, delim, false );
    281        }
    282
    283      public static void join( StringBuffer buffer, Collection collection, String delim, boolean printNull )
    284        {
    285        int count = 0;
    286
    287        for( Object s : collection )
    288          {
    289          if( count != 0 )
    290            buffer.append( delim );
    291
    292          if( printNull || s != null )
    293            buffer.append( s );
    294
    295          count++;
    296          }
    297        }
    298
    299      public static String[] removeNulls( String... strings )
    300        {
    301        List<String> list = new ArrayList<String>();
    302
    303        for( String string : strings )
    304          {
    305          if( string != null )
    306            list.add( string );
    307          }
    308
    309        return list.toArray( new String[ list.size() ] );
    310        }
    311
    312      public static Collection<String> quote( Collection<String> collection, String quote )
    313        {
    314        List<String> list = new ArrayList<String>();
    315
    316        for( String string : collection )
    317          list.add( quote + string + quote );
    318
    319        return list;
    320        }
    321
    322      public static String print( Collection collection, String delim )
    323        {
    324        StringBuffer buffer = new StringBuffer();
    325
    326        print( buffer, collection, delim );
    327
    328        return buffer.toString();
    329        }
    330
    331      public static void print( StringBuffer buffer, Collection collection, String delim )
    332        {
    333        int count = 0;
    334
    335        for( Object s : collection )
    336          {
    337          if( count != 0 )
    338            buffer.append( delim );
    339
    340          buffer.append( "[" );
    341          buffer.append( s );
    342          buffer.append( "]" );
    343
    344          count++;
    345          }
    346        }
    347
    348      /**
    349       * This method attempts to remove any username and password from the given url String.
    350       *
    351       * @param url
    352       * @return a String
    353       */
    354      public static String sanitizeUrl( String url )
    355        {
    356        if( url == null )
    357          return null;
    358
    359        return url.replaceAll( "(?<=//).*:.*@", "" );
    360        }
    361
    362      /**
    363       * This method attempts to remove duplicate consecutive forward slashes from the given url.
    364       *
    365       * @param url
    366       * @return a String
    367       */
    368      public static String normalizeUrl( String url )
    369        {
    370        if( url == null )
    371          return null;
    372
    373        return url.replaceAll( "([^:]/)/{2,}", "$1/" );
    374        }
    375
    376      /**
    377       * This method returns the {@link Object#toString()} of the given object, or an empty String if the object
    378       * is null.
    379       *
    380       * @param object
    381       * @return a String
    382       */
    383      public static String toNull( Object object )
    384        {
    385        if( object == null )
    386          return "";
    387
    388        return object.toString();
    389        }
    390
    391      /**
    392       * This method truncates the given String value to the given size, but appends an ellipse ("...") if the
    393       * String is larger than maxSize.
    394       *
    395       * @param string
    396       * @param maxSize
    397       * @return a String
    398       */
    399      public static String truncate( String string, int maxSize )
    400        {
    401        string = toNull( string );
    402
    403        if( string.length() <= maxSize )
    404          return string;
    405
    406        return String.format( "%s...", string.subSequence( 0, maxSize - 3 ) );
    407        }
    408
    409      public static String printGraph( SimpleDirectedGraph graph )
    410        {
    411        StringWriter writer = new StringWriter();
    412
    413        printGraph( writer, graph );
    414
    415        return writer.toString();
    416        }
    417
    418      public static void printGraph( PrintStream out, SimpleDirectedGraph graph )
    419        {
    420        PrintWriter printWriter = new PrintWriter( out );
    421
    422        printGraph( printWriter, graph );
    423        }
    424
    425      public static void printGraph( String filename, SimpleDirectedGraph graph )
    426        {
    427        try
    428          {
    429          Writer writer = new FileWriter( filename );
    430
    431          try
    432            {
    433            printGraph( writer, graph );
    434            }
    435          finally
    436            {
    437            writer.close();
    438            }
    439          }
    440        catch( IOException exception )
    441          {
    442          LOG.error( "failed printing graph to {}, with exception: {}", filename, exception );
    443          }
    444        }
    445
    446      @SuppressWarnings({"unchecked"})
    447      private static void printGraph( Writer writer, SimpleDirectedGraph graph )
    448        {
    449        DOTExporter dot = new DOTExporter( new IntegerNameProvider(), new VertexNameProvider()
    450        {
    451        public String getVertexName( Object object )
    452          {
    453          if( object == null )
    454            return "none";
    455
    456          return object.toString().replaceAll( "\"", "\'" );
    457          }
    458        }, new EdgeNameProvider<Object>()
    459        {
    460        public String getEdgeName( Object object )
    461          {
    462          if( object == null )
    463            return "none";
    464
    465          return object.toString().replaceAll( "\"", "\'" );
    466          }
    467        }
    468        );
    469
    470        dot.export( writer, graph );
    471        }
    472
    473      public static void printMatrix( PrintStream out, SimpleDirectedGraph<FlowElement, Scope> graph )
    474        {
    475        new MatrixExporter().exportAdjacencyMatrix( new PrintWriter( out ), graph );
    476        }
    477
    478      /**
    479       * This method removes all nulls from the given List.
    480       *
    481       * @param list
    482       */
    483      @SuppressWarnings({"StatementWithEmptyBody"})
    484      public static void removeAllNulls( List list )
    485        {
    486        while( list.remove( null ) )
    487          ;
    488        }
    489
    490      /**
    491       * Allows for custom trace fields on Pipe, Tap, and Scheme types
    492       *
    493       * @deprecated see {@link cascading.util.TraceUtil#setTrace(Object, String)}
    494       */
    495      @Deprecated
    496      public static void setTrace( Object object, String trace )
    497        {
    498        TraceUtil.setTrace( object, trace );
    499        }
    500
    501      /**
    502       * @deprecated see {@link cascading.util.TraceUtil#captureDebugTrace(Object)}
    503       */
    504      @Deprecated
    505      public static String captureDebugTrace( Class type )
    506        {
    507        return TraceUtil.captureDebugTrace( type );
    508        }
    509
    510      public static String formatTrace( final Pipe pipe, String message )
    511        {
    512        return TraceUtil.formatTrace( pipe, message );
    513        }
    514
    515      /**
    516       * @deprecated see {@link cascading.util.TraceUtil#formatTrace(cascading.tap.Tap, String)}
    517       */
    518      @Deprecated
    519      public static String formatTrace( final Tap tap, String message )
    520        {
    521        return TraceUtil.formatTrace( tap, message );
    522        }
    523
    524      /**
    525       * @deprecated see {@link cascading.util.TraceUtil#formatTrace(cascading.scheme.Scheme, String)}
    526       */
    527      @Deprecated
    528      public static String formatTrace( final Scheme scheme, String message )
    529        {
    530        return TraceUtil.formatTrace( scheme, message );
    531        }
    532
    533      /**
    534       * @deprecated see {@link cascading.util.TraceUtil#formatTrace(cascading.operation.Operation, String)}
    535       */
    536      @Deprecated
    537      public static String formatTrace( Operation operation, String message )
    538        {
    539        return TraceUtil.formatTrace( operation, message );
    540        }
    541
    542      public static void writeDOT( Writer writer, SimpleDirectedGraph graph, IntegerNameProvider vertexIdProvider, VertexNameProvider vertexNameProvider, EdgeNameProvider edgeNameProvider )
    543        {
    544        new DOTExporter( vertexIdProvider, vertexNameProvider, edgeNameProvider ).export( writer, graph );
    545        }
    546
    547      public static boolean isEmpty( String string )
    548        {
    549        return string == null || string.isEmpty();
    550        }
    551
    552      private static String[] findSplitName( String path )
    553        {
    554        String separator = "/";
    555
    556        if( path.lastIndexOf( "/" ) < path.lastIndexOf( "\\" ) )
    557          separator = "\\\\";
    558
    559        String[] split = path.split( separator );
    560
    561        path = split[ split.length - 1 ];
    562
    563        path = path.substring( 0, path.lastIndexOf( '.' ) ); // remove .jar
    564
    565        return path.split( "-(?=\\d)", 2 );
    566        }
    567
    568      public static String findVersion( String path )
    569        {
    570        if( path == null || path.isEmpty() )
    571          return null;
    572
    573        String[] split = findSplitName( path );
    574
    575        if( split.length == 2 )
    576          return split[ 1 ];
    577
    578        return null;
    579        }
    580
    581      public static String findName( String path )
    582        {
    583        if( path == null || path.isEmpty() )
    584          return null;
    585
    586        String[] split = findSplitName( path );
    587
    588        if( split.length == 0 )
    589          return null;
    590
    591        return split[ 0 ];
    592        }
    593
    594      public static long getSourceModified( Object confCopy, Iterator<Tap> values, long sinkModified ) throws IOException
    595        {
    596        long sourceModified = 0;
    597
    598        while( values.hasNext() )
    599          {
    600          Tap source = values.next();
    601
    602          if( source instanceof MultiSourceTap )
    603            return getSourceModified( confCopy, ( (MultiSourceTap) source ).getChildTaps(), sinkModified );
    604
    605          sourceModified = source.getModifiedTime( confCopy );
    606
    607          // source modified returns zero if does not exist
    608          // this should minimize number of times we touch any file meta-data server
    609          if( sourceModified == 0 && !source.resourceExists( confCopy ) )
    610            throw new FlowException( "source does not exist: " + source );
    611
    612          if( sinkModified < sourceModified )
    613            return sourceModified;
    614          }
    615
    616        return sourceModified;
    617        }
    618
    619      public static long getSinkModified( Object config, Collection<Tap> sinks ) throws IOException
    620        {
    621        long sinkModified = Long.MAX_VALUE;
    622
    623        for( Tap sink : sinks )
    624          {
    625          if( sink.isReplace() || sink.isUpdate() )
    626            sinkModified = -1L;
    627          else
    628            {
    629            if( !sink.resourceExists( config ) )
    630              sinkModified = 0L;
    631            else
    632              sinkModified = Math.min( sinkModified, sink.getModifiedTime( config ) ); // return youngest mod date
    633            }
    634          }
    635        return sinkModified;
    636        }
    637
    638      public static String getTypeName( Type type )
    639        {
    640        if( type == null )
    641          return null;
    642
    643        return type instanceof Class ? ( (Class) type ).getCanonicalName() : type.toString();
    644        }
    645
    646      public static String getSimpleTypeName( Type type )
    647        {
    648        if( type == null )
    649          return null;
    650
    651        return type instanceof Class ? ( (Class) type ).getSimpleName() : type.toString();
    652        }
    653
    654      public static String[] typeNames( Type[] types )
    655        {
    656        String[] names = new String[ types.length ];
    657
    658        for( int i = 0; i < types.length; i++ )
    659          names[ i ] = getTypeName( types[ i ] );
    660
    661        return names;
    662        }
    663
    664      public static String[] simpleTypeNames( Type[] types )
    665        {
    666        String[] names = new String[ types.length ];
    667
    668        for( int i = 0; i < types.length; i++ )
    669          names[ i ] = getSimpleTypeName( types[ i ] );
    670
    671        return names;
    672        }
    673
    674      public static boolean containsNull( Object[] values )
    675        {
    676        for( Object value : values )
    677          {
    678          if( value == null )
    679            return true;
    680          }
    681
    682        return false;
    683        }
    684
    685      public static void safeSleep( long durationMillis )
    686        {
    687        try
    688          {
    689          Thread.sleep( durationMillis );
    690          }
    691        catch( InterruptedException exception )
    692          {
    693          // do nothing
    694          }
    695        }
    696
    697      /**
    698       * Converts a given comma separated String of Exception names into a List of classes.
    699       * ClassNotFound exceptions are ignored if no warningMessage is given, otherwise logged as a warning.
    700       *
    701       * @param classNames A comma separated String of Exception names.
    702       * @return List of Exception classes.
    703       */
    704      public static Set<Class<? extends Exception>> asClasses( String classNames, String warningMessage )
    705        {
    706        Set<Class<? extends Exception>> exceptionClasses = new HashSet<Class<? extends Exception>>();
    707        String[] split = classNames.split( "," );
    708
    709        // possibly user provided type, load from context
    710        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
    711
    712        for( String className : split )
    713          {
    714          if( className != null )
    715            className = className.trim();
    716
    717          if( isEmpty( className ) )
    718            continue;
    719
    720          try
    721            {
    722            Class<? extends Exception> exceptionClass = contextClassLoader.loadClass( className ).asSubclass( Exception.class );
    723
    724            exceptionClasses.add( exceptionClass );
    725            }
    726          catch( ClassNotFoundException exception )
    727            {
    728            if( !Util.isEmpty( warningMessage ) )
    729              LOG.warn( "{}: {}", warningMessage, className );
    730            }
    731          }
    732
    733        return exceptionClasses;
    734        }
    735
    736      public interface RetryOperator<T>
    737        {
    738        T operate() throws Exception;
    739
    740        boolean rethrow( Exception exception );
    741        }
    742
    743      public static <T> T retry( Logger logger, int retries, int secondsDelay, String message, RetryOperator<T> operator ) throws Exception
    744        {
    745        Exception saved = null;
    746
    747        for( int i = 0; i < retries; i++ )
    748          {
    749          try
    750            {
    751            return operator.operate();
    752            }
    753          catch( Exception exception )
    754            {
    755            if( operator.rethrow( exception ) )
    756              {
    757              logger.warn( message + ", but not retrying", exception );
    758
    759              throw exception;
    760              }
    761
    762            saved = exception;
    763
    764            logger.warn( message + ", attempt: " + ( i + 1 ), exception );
    765
    766            try
    767              {
    768              Thread.sleep( secondsDelay * 1000 );
    769              }
    770            catch( InterruptedException exception1 )
    771              {
    772              // do nothing
    773              }
    774            }
    775          }
    776
    777        logger.warn( message + ", done retrying after attempts: " + retries, saved );
    778
    779        throw saved;
    780        }
    781
    782      public static Object createProtectedObject( Class type, Object[] parameters, Class[] parameterTypes )
    783        {
    784        try
    785          {
    786          Constructor constructor = type.getDeclaredConstructor( parameterTypes );
    787
    788          constructor.setAccessible( true );
    789
    790          return constructor.newInstance( parameters );
    791          }
    792        catch( Exception exception )
    793          {
    794          LOG.error( "unable to instantiate type: {}, with exception: {}", type.getName(), exception );
    795
    796          throw new FlowException( "unable to instantiate type: " + type.getName(), exception );
    797          }
    798        }
    799
    800      public static boolean hasClass( String typeString )
    801        {
    802        try
    803          {
    804          Util.class.getClassLoader().loadClass( typeString );
    805
    806          return true;
    807          }
    808        catch( ClassNotFoundException exception )
    809          {
    810          return false;
    811          }
    812        }
    813
    814      public static <T> T newInstance( String className, Object... parameters )
    815        {
    816        try
    817          {
    818          Class<T> type = (Class<T>) Util.class.getClassLoader().loadClass( className );
    819
    820          return newInstance( type, parameters );
    821          }
    822        catch( ClassNotFoundException exception )
    823          {
    824          throw new CascadingException( "unable to load class: " + className, exception );
    825          }
    826        }
    827
    828      public static <T> T newInstance( Class<T> target, Object... parameters )
    829        {
    830        // using Expression makes sure that constructors using sub-types properly work, otherwise we get a
    831        // NoSuchMethodException.
    832        Expression expr = new Expression( target, "new", parameters );
    833
    834        try
    835          {
    836          return (T) expr.getValue();
    837          }
    838        catch( Exception exception )
    839          {
    840          throw new CascadingException( "unable to create new instance: " + target.getName() + "(" + Arrays.toString( parameters ) + ")", exception );
    841          }
    842        }
    843
    844      public static Object invokeStaticMethod( String typeString, String methodName, Object[] parameters, Class[] parameterTypes )
    845        {
    846        try
    847          {
    848          Class type = Util.class.getClassLoader().loadClass( typeString );
    849
    850          return invokeStaticMethod( type, methodName, parameters, parameterTypes );
    851          }
    852        catch( ClassNotFoundException exception )
    853          {
    854          throw new CascadingException( "unable to load class: " + typeString, exception );
    855          }
    856        }
    857
    858      public static Object invokeStaticMethod( Class type, String methodName, Object[] parameters, Class[] parameterTypes )
    859        {
    860        try
    861          {
    862          Method method = type.getDeclaredMethod( methodName, parameterTypes );
    863
    864          method.setAccessible( true );
    865
    866          return method.invoke( null, parameters );
    867          }
    868        catch( Exception exception )
    869          {
    870          throw new CascadingException( "unable to invoke static method: " + type.getName() + "." + methodName, exception );
    871          }
    872        }
    873
    874      public static Object invokeInstanceMethod( Object target, String methodName, Object[] parameters, Class[] parameterTypes )
    875        {
    876        try
    877          {
    878          Method method = target.getClass().getMethod( methodName, parameterTypes );
    879
    880          method.setAccessible( true );
    881
    882          return method.invoke( target, parameters );
    883          }
    884        catch( Exception exception )
    885          {
    886          throw new CascadingException( "unable to invoke instance method: " + target.getClass().getName() + "." + methodName, exception );
    887          }
    888        }
    889
    890      public static <R> R returnInstanceFieldIfExistsSafe( Object target, String fieldName )
    891        {
    892        try
    893          {
    894          return returnInstanceFieldIfExists( target, fieldName );
    895          }
    896        catch( Exception exception )
    897          {
    898          // do nothing
    899          return null;
    900          }
    901        }
    902
    903      public static Object invokeConstructor( String className, Object[] parameters, Class[] parameterTypes )
    904        {
    905        try
    906          {
    907          Class type = Util.class.getClassLoader().loadClass( className );
    908
    909          return invokeConstructor( type, parameters, parameterTypes );
    910          }
    911        catch( ClassNotFoundException exception )
    912          {
    913          throw new CascadingException( "unable to load class: " + className, exception );
    914          }
    915        }
    916
    917      public static <T> T invokeConstructor( Class<T> target, Object[] parameters, Class[] parameterTypes )
    918        {
    919        try
    920          {
    921          Constructor<T> constructor = target.getConstructor( parameterTypes );
    922
    923          constructor.setAccessible( true );
    924
    925          return constructor.newInstance( parameters );
    926          }
    927        catch( Exception exception )
    928          {
    929          throw new CascadingException( "unable to create new instance: " + target.getName() + "(" + Arrays.toString( parameters ) + ")", exception );
    930          }
    931        }
    932
    933      public static <R> R returnInstanceFieldIfExists( Object target, String fieldName )
    934        {
    935        try
    936          {
    937          Class<?> type = target.getClass();
    938          Field field = getDeclaredField( fieldName, type );
    939
    940          field.setAccessible( true );
    941
    942          return (R) field.get( target );
    943          }
    944        catch( Exception exception )
    945          {
    946          throw new CascadingException( "unable to get instance field: " + target.getClass().getName() + "." + fieldName, exception );
    947          }
    948        }
    949
    950      public static <R> void setInstanceFieldIfExists( Object target, String fieldName, R value )
    951        {
    952        try
    953          {
    954          Class<?> type = target.getClass();
    955          Field field = getDeclaredField( fieldName, type );
    956
    957          field.setAccessible( true );
    958
    959          field.set( target, value );
    960          }
    961        catch( Exception exception )
    962          {
    963          throw new CascadingException( "unable to set instance field: " + target.getClass().getName() + "." + fieldName, exception );
    964          }
    965        }
    966
    967      private static Field getDeclaredField( String fieldName, Class<?> type )
    968        {
    969        if( type == Object.class )
    970          {
    971          if( LOG.isDebugEnabled() )
    972            LOG.debug( "did not find {} field on {}", fieldName, type.getName() );
    973
    974          return null;
    975          }
    976
    977        try
    978          {
    979          return type.getDeclaredField( fieldName );
    980          }
    981        catch( NoSuchFieldException exception )
    982          {
    983          return getDeclaredField( fieldName, type.getSuperclass() );
    984          }
    985        }
    986
    987      @Deprecated
    988      public static String makeTempPath( String name )
    989        {
    990        if( name == null || name.isEmpty() )
    991          throw new IllegalArgumentException( "name may not be null or empty " );
    992
    993        name = cleansePathName( name.substring( 0, name.length() < 25 ? name.length() : 25 ) );
    994
    995        return name + "/" + (int) ( Math.random() * 100000 ) + "/";
    996        }
    997
    998      public static String makePath( String prefix, String name )
    999        {
    1000        if( name == null || name.isEmpty() )
    1001          throw new IllegalArgumentException( "name may not be null or empty " );
    1002
    1003        if( prefix == null || prefix.isEmpty() )
    1004          prefix = Long.toString( (long) ( Math.random() * 10000000000L ) );
    1005
    1006        name = cleansePathName( name.substring( 0, name.length() < 25 ? name.length() : 25 ) );
    1007
    1008        return prefix + "/" + name + "/";
    1009        }
    1010
    1011      public static String cleansePathName( String name )
    1012        {
    1013        return name.replaceAll( "\\s+|\\*|\\+|/+", "_" );
    1014        }
    1015      }